Rabu, 15 Juli 2026

Belajar react 1

Baik. Saya akan mengajarkan React dengan pendekatan yang disesuaikan dengan pengalaman Anda sebagai programmer PHP. Jadi kita tidak mulai dari teori yang rumit, tetapi dengan membandingkannya dengan yang sudah Anda kuasai.

Rencana Belajar React

  1. ✅ Apa itu React
  2. ✅ Struktur proyek React
  3. ➜ JSX (pelajaran hari ini)
  4. Komponen
  5. Props
  6. State
  7. Event
  8. Form
  9. Routing
  10. React + Supabase

Pelajaran 1: JSX

JSX adalah cara menulis HTML di dalam JavaScript.

Misalnya di PHP Anda biasa menulis:

<h1>Assalamu'alaikum</h1>
<p>Selamat datang di Bimbel.</p>

Di React:

function App() {
  return (
    <div>
      <h1>Assalamu'alaikum</h1>
      <p>Selamat datang di Bimbel.</p>
    </div>
  );
}

Yang penting diperhatikan adalah:

return (
   ...
)

Artinya fungsi App() mengembalikan tampilan.


Kenapa harus ada <div>?

Di React, satu komponen harus mengembalikan satu elemen induk.

❌ Salah

return (
    <h1>Judul</h1>
    <p>Isi</p>
)

Karena ada dua elemen sejajar.

✅ Benar

return (
    <div>
        <h1>Judul</h1>
        <p>Isi</p>
    </div>
)

Menampilkan variabel

PHP:

<?php
$nama = "Abu Unais";
?>

<p><?= $nama ?></p>

React:

function App() {
    const nama = "Abu Unais";

    return (
        <p>{nama}</p>
    );
}

Di React, variabel ditampilkan dengan kurung kurawal {}.


Menampilkan hasil perhitungan

function App() {
    return (
        <h1>{10 + 5}</h1>
    );
}

Hasilnya:

15

Di dalam {} Anda bisa menulis ekspresi JavaScript.


Perbedaan dengan HTML

Ada beberapa perbedaan kecil.

HTML:

<label for="nama">Nama</label>

React:

<label htmlFor="nama">Nama</label>

Karena for adalah kata khusus di JavaScript.

Begitu juga:

HTML:

<input class="form-control">

React:

<input className="form-control" />

Menjadi className.


Tugas kecil

Buatlah komponen yang menampilkan:

Bismillah

Nama : Abu Unais
Usia : 40 Tahun
Pekerjaan : Programmer PHP

Gunakan:

  • const untuk menyimpan data.
  • {} untuk menampilkan variabel.

Tidak perlu khawatir jika salah. Justru dari latihan-latihan kecil seperti ini Anda akan cepat terbiasa membaca dan menulis kode React.

Besok atau setelah Anda selesai mencoba latihan ini, kita akan masuk ke komponen, yaitu konsep paling penting dalam React. Setelah memahami komponen, Anda akan mulai bisa membaca kode proyek Lovable yang sedang Anda kerjakan.

Minggu, 28 Juni 2026

test

Hadits yang Anda maksud diriwayatkan oleh Imam al-Bukhari dan Imam Muslim dari Hakim bin Hizam radhiyallahu 'anhu.

Dengan harakat:

عَنْ حَكِيمِ بْنِ حِزَامٍ رَضِيَ اللَّهُ عَنْهُ، عَنِ النَّبِيِّ ﷺ قَالَ:

«الْبَيِّعَانِ بِالْخِيَارِ مَا لَمْ يَتَفَرَّقَا، فَإِنْ صَدَقَا وَبَيَّنَا بُورِكَ لَهُمَا فِي بَيْعِهِمَا، وَإِنْ كَذَبَا وَكَتَمَا مُحِقَتْ بَرَكَةُ بَيْعِهِمَا.»

Terjemah:

Dari Hakim bin Hizam radhiyallahu 'anhu, dari Nabi ﷺ, beliau bersabda:

"Penjual dan pembeli masih memiliki hak memilih (melanjutkan atau membatalkan transaksi) selama mereka belum berpisah. Jika keduanya jujur dan menjelaskan (keadaan barang dengan apa adanya), niscaya keduanya akan diberi keberkahan dalam jual beli mereka. Namun jika keduanya berdusta dan menyembunyikan (cacat barang), maka dihapuslah keberkahan jual beli mereka."

Referensi:

  • Shahih al-Bukhari, no. 2079.
  • Shahih Muslim, no. 1532.

Hadits ini menjadi salah satu dalil utama dalam Islam bahwa kejujuran dan keterbukaan dalam muamalah merupakan sebab turunnya keberkahan, sedangkan kebohongan dan menyembunyikan cacat barang dapat menghilangkan keberkahan meskipun transaksi tersebut secara lahiriah menghasilkan keuntungan.

Jumat, 19 Juni 2026

floating windows

Jika yang Anda maksud adalah tombol ⋮ (three dots) yang ketika diklik muncul kotak menu mengambang seperti WhatsApp, Telegram, atau aplikasi mobile banking, maka Bootstrap 5 sudah menyediakan komponen Dropdown yang sangat cocok.

1. Tampilan Three Dot Vertikal

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">

<div class="dropdown">

    <button class="btn btn-light border-0"
            data-bs-toggle="dropdown">

        <i class="bi bi-three-dots-vertical"></i>

    </button>

    <ul class="dropdown-menu dropdown-menu-end shadow">

        <li>
            <a class="dropdown-item" href="#">
                <i class="bi bi-pencil me-2"></i>
                Edit
            </a>
        </li>

        <li>
            <a class="dropdown-item" href="#">
                <i class="bi bi-printer me-2"></i>
                Cetak
            </a>
        </li>

        <li>
            <a class="dropdown-item" href="#">
                <i class="bi bi-whatsapp me-2"></i>
                Kirim WA
            </a>
        </li>

        <li><hr class="dropdown-divider"></li>

        <li>
            <a class="dropdown-item text-danger" href="#">
                <i class="bi bi-trash me-2"></i>
                Hapus
            </a>
        </li>

    </ul>

</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>

Hasilnya:

      ⋮
      │
 ┌─────────────┐
 │ ✏ Edit      │
 │ 🖨 Cetak     │
 │ 📱 Kirim WA  │
 ├─────────────┤
 │ 🗑 Hapus     │
 └─────────────┘

2. Versi Lebih Elegan (Rounded & Floating)

Tambahkan CSS:

.dropdown-menu{
    border:none;
    border-radius:18px;
    padding:10px;
    min-width:220px;
    box-shadow:0 10px 30px rgba(0,0,0,.15);
}

.dropdown-item{
    border-radius:12px;
    padding:10px 14px;
}

.dropdown-item:hover{
    background:#f5f7fa;
}

Tampilannya mirip aplikasi mobile modern.


3. Floating Menu di Kartu Siswa

Contoh pada kartu siswa:

<div class="card shadow-sm rounded-4">

    <div class="card-body">

        <div class="d-flex justify-content-between">

            <div>
                <h5 class="mb-0">Ahmad Fauzan</h5>
                <small class="text-muted">
                    Tahsin A
                </small>
            </div>

            <div class="dropdown">

                <button
                    class="btn btn-light rounded-circle"
                    data-bs-toggle="dropdown">

                    <i class="bi bi-three-dots-vertical"></i>

                </button>

                <ul class="dropdown-menu dropdown-menu-end">

                    <li>
                        <a class="dropdown-item" href="#">
                            Detail
                        </a>
                    </li>

                    <li>
                        <a class="dropdown-item" href="#">
                            Edit
                        </a>
                    </li>

                    <li>
                        <a class="dropdown-item" href="#">
                            Kartu SPP
                        </a>
                    </li>

                    <li>
                        <a class="dropdown-item" href="#">
                            Pembayaran
                        </a>
                    </li>

                </ul>

            </div>

        </div>

    </div>

</div>

4. Floating Action Menu (Seperti Android)

Jika ingin benar-benar mengambang di kanan bawah layar:

<div class="dropup position-fixed bottom-0 end-0 m-4">

    <button class="btn btn-primary rounded-circle shadow"
            style="width:60px;height:60px"
            data-bs-toggle="dropdown">

        <i class="bi bi-three-dots fs-4"></i>

    </button>

    <ul class="dropdown-menu dropdown-menu-end shadow">

        <li><a class="dropdown-item" href="#">Tambah Siswa</a></li>
        <li><a class="dropdown-item" href="#">Tagihan Baru</a></li>
        <li><a class="dropdown-item" href="#">Laporan</a></li>

    </ul>

</div>

Tampilannya:

                   ┌──────────┐
                   │ Tambah   │
                   │ Tagihan  │
                   │ Laporan  │
                   └──────────┘

                        ●

Untuk aplikasi TPA/Bimbel yang sedang Anda buat, saya sarankan menggunakan Dropdown Bootstrap dengan dropdown-menu-end dan sudut membulat (rounded-4). Model ini paling mirip aplikasi profesional seperti Dana, BSI Mobile, atau MyTelkomsel, ringan, dan tidak perlu JavaScript tambahan selain Bootstrap.

Rabu, 17 Juni 2026

tampilkan pilihan

Masalahnya ada pada kondisi ini:

if($_GET['page']=='dataSiswa' or 'siswaNonAktif')

Bagian:

or 'siswaNonAktif'

selalu dianggap TRUE oleh PHP, sehingga kondisi menjadi tidak benar sesuai yang Anda inginkan.

Gunakan seperti ini:

<?php
if($_GET['page']=='dataSiswa' || $_GET['page']=='siswaNonAktif'){
?>
    <i class="bi bi-people text-primary"></i>
<?php
}else{
?>
    <i class="bi bi-people"></i>
<?php
}
?>

Atau yang lebih rapi:

<i class="bi bi-people <?=(isset($_GET['page']) && ($_GET['page']=='dataSiswa' || $_GET['page']=='siswaNonAktif')) ? 'text-primary' : '';?>"></i>

Sehingga menu Siswa akan berwarna biru ketika:

?page=dataSiswa

atau

?page=siswaNonAktif

Versi yang lebih aman

Untuk menghindari warning ketika $_GET['page'] belum ada:

<?php
$page = $_GET['page'] ?? '';

if(in_array($page,['dataSiswa','siswaNonAktif'])){
    echo '<i class="bi bi-people text-primary"></i>';
}else{
    echo '<i class="bi bi-people"></i>';
}
?>

Cara in_array() sangat cocok jika nanti submenu bertambah, misalnya:

in_array($page,[
    'dataSiswa',
    'siswaNonAktif',
    'siswaPindah',
    'siswaLulus'
])

jadi semua halaman yang berkaitan dengan siswa otomatis membuat ikon menu Siswa berwarna biru.

Minggu, 14 Juni 2026

Tanggal dengan rentang waktu tanpa submot

Bisa. Bahkan ini sangat umum digunakan pada dashboard laporan keuangan.

Caranya adalah membuat 2 input tanggal:

<div class="row">
    <div class="col-6">
        <input type="date" id="tgl_awal" class="form-control">
    </div>

    <div class="col-6">
        <input type="date" id="tgl_akhir" class="form-control">
    </div>
</div>

<div id="hasil"></div>

Javascript (Tanpa Submit)

Setiap kali salah satu tanggal berubah, data langsung diambil.

<script>

function loadData(){

    let awal  = document.getElementById('tgl_awal').value;
    let akhir = document.getElementById('tgl_akhir').value;

    if(awal && akhir){

        fetch(`laporan.php?awal=${awal}&akhir=${akhir}`)
        .then(response => response.text())
        .then(data => {

            document.getElementById('hasil').innerHTML = data;

        });

    }

}

document.getElementById('tgl_awal')
.addEventListener('change', loadData);

document.getElementById('tgl_akhir')
.addEventListener('change', loadData);

</script>

PHP

Misal file laporan.php

<?php

include 'koneksi.php';

$awal  = $_GET['awal'];
$akhir = $_GET['akhir'];

$query = mysqli_query($koneksi,"
SELECT *
FROM pembayaran
WHERE tanggal BETWEEN '$awal' AND '$akhir'
ORDER BY tanggal
");

while($row=mysqli_fetch_assoc($query)){

    echo "<div>";
    echo $row['tanggal']." - ";
    echo $row['nama']." - ";
    echo number_format($row['jumlah']);
    echo "</div>";

}

Untuk Dashboard Ringkasan

Misal Anda ingin otomatis mengubah:

  • Total pemasukan
  • Jumlah transaksi
  • Total tunggakan

Gunakan JSON.

PHP

<?php

include 'koneksi.php';

$awal  = $_GET['awal'];
$akhir = $_GET['akhir'];

$q = mysqli_query($koneksi,"
SELECT
SUM(jumlah) total,
COUNT(*) transaksi
FROM pembayaran
WHERE tanggal BETWEEN '$awal' AND '$akhir'
");

$data = mysqli_fetch_assoc($q);

echo json_encode($data);

Javascript

function loadData(){

    let awal  = $('#tgl_awal').val();
    let akhir = $('#tgl_akhir').val();

    if(awal && akhir){

        fetch(`summary.php?awal=${awal}&akhir=${akhir}`)
        .then(res => res.json())
        .then(data => {

            $('#total').html(
                'Rp ' + Number(data.total).toLocaleString('id-ID')
            );

            $('#transaksi').html(
                data.transaksi
            );

        });

    }
}

Lebih Elegan Lagi: Date Range Picker

Daripada dua input tanggal, gunakan satu input seperti:

01/06/2026 - 30/06/2026

Contohnya:

<input type="text" id="daterange" class="form-control">

Menggunakan plugin:

  • Date Range Picker
  • Flatpickr
  • Litepicker

Tampilannya seperti aplikasi kasir profesional dan laporan keuangan modern.

Contoh:

┌─────────────────────────┐
│ 📅 01 Jun - 30 Jun 2026 │
└─────────────────────────┘

Begitu rentang tanggal dipilih, AJAX langsung memuat ulang:

  • Ringkasan pemasukan
  • Grafik
  • Daftar pembayaran
  • Jumlah siswa yang membayar

tanpa tombol submit dan tanpa reload halaman. Ini adalah pola yang paling cocok untuk dashboard TPA/Bimbel yang sedang Anda bangun.

ganti data otomatis tanpa submit

Cara yang Anda inginkan adalah menggunakan AJAX atau Fetch API. Jadi ketika user mengganti tanggal pada <input type="date">, JavaScript langsung mengirim request ke PHP dan memperbarui data tanpa reload halaman dan tanpa tombol submit.

Contoh Sederhana

HTML

<input type="date" id="tanggal" class="form-control">

<div id="hasil">
    Silakan pilih tanggal
</div>

Javascript

<script>
document.getElementById('tanggal').addEventListener('change', function(){

    let tanggal = this.value;

    fetch('ambil_data.php?tanggal=' + tanggal)
    .then(response => response.text())
    .then(data => {

        document.getElementById('hasil').innerHTML = data;

    });

});
</script>

Ketika tanggal berubah, browser akan otomatis memanggil:

ambil_data.php?tanggal=2026-06-14

PHP (ambil_data.php)

<?php

$tanggal = $_GET['tanggal'];

include 'koneksi.php';

$query = mysqli_query($koneksi,"
SELECT *
FROM pembayaran
WHERE tanggal='$tanggal'
");

while($row = mysqli_fetch_assoc($query)){

    echo "<div>";
    echo $row['nama'];
    echo " - ";
    echo $row['jumlah'];
    echo "</div>";

}

Jika Menggunakan Bootstrap Card

Misal Anda punya ringkasan:

<div class="card">
    <div class="card-body">

        <h5>Total Pemasukan</h5>

        <h2 id="pemasukan">
            Rp0
        </h2>

    </div>
</div>

Javascript:

document.getElementById('tanggal').addEventListener('change', function(){

    let tanggal = this.value;

    fetch('summary.php?tanggal='+tanggal)
    .then(response => response.json())
    .then(data => {

        document.getElementById('pemasukan').innerHTML =
            'Rp' + Number(data.total).toLocaleString('id-ID');

    });

});

PHP:

<?php

$tanggal = $_GET['tanggal'];

include 'koneksi.php';

$q = mysqli_query($koneksi,"
SELECT SUM(jumlah) as total
FROM pembayaran
WHERE tanggal='$tanggal'
");

$data = mysqli_fetch_assoc($q);

echo json_encode($data);

Lebih Praktis dengan jQuery

Jika sudah menggunakan jQuery:

$('#tanggal').change(function(){

    let tanggal = $(this).val();

    $('#hasil').load(
        'ambil_data.php?tanggal='+tanggal
    );

});

Sangat singkat dan banyak dipakai di aplikasi admin.


Yang Paling Cocok Untuk Dashboard TPA Anda

Pada dashboard yang Anda buat:

  • User memilih tanggal.
  • Event change() terpanggil.
  • AJAX mengambil data:
    • Total pemasukan
    • Jumlah transaksi
    • Belum bayar
    • Daftar pembayaran
  • Card-card dashboard diperbarui otomatis.
  • Tidak ada tombol submit.
  • Tidak ada reload halaman.

Hasilnya terasa seperti aplikasi Android native meskipun hanya menggunakan PHP + Bootstrap 5.

Minggu, 07 Juni 2026

perbaikan

Pada script Anda sebenarnya sudah hampir benar. Yang perlu ditambahkan adalah:

  1. Preview foto harus memiliki id.
  2. Saat file dipilih, tampilkan preview.
  3. Upload otomatis menggunakan AJAX.
  4. Simpan path gambar ke database.
  5. Tidak perlu tombol upload.

1. Ubah bagian foto menjadi:

<div class="profile-wrapper">

    <img src="<?=$row['gambar'];?>"
         id="preview"
         class="profile-photo">

    <button type="button"
            class="camera-btn"
            onclick="document.getElementById('foto').click()">

        <i class="bi bi-camera-fill"></i>

    </button>

    <input type="file"
           id="foto"
           name="foto"
           accept="image/*"
           style="display:none;">

    <input type="hidden"
           id="id_siswa"
           value="<?=$idSiswa;?>">

</div>

<div id="uploadStatus"
     class="small mt-2 text-secondary">
</div>

2. Tambahkan script sebelum </body>

<script>

$('#foto').on('change', function(){

    let file = this.files[0];

    if(!file){
        return;
    }

    /*
    |--------------------------------------------------------------------------
    | Preview Langsung
    |--------------------------------------------------------------------------
    */
    let previewUrl = URL.createObjectURL(file);

    $('#preview').attr('src', previewUrl);

    $('#uploadStatus').html(
        '<span class="text-primary">Mengupload foto...</span>'
    );

    /*
    |--------------------------------------------------------------------------
    | Upload Otomatis
    |--------------------------------------------------------------------------
    */
    let formData = new FormData();

    formData.append('foto', file);
    formData.append(
        'id_siswa',
        $('#id_siswa').val()
    );

    $.ajax({

        url:'upload_foto_siswa.php',

        type:'POST',

        data:formData,

        contentType:false,

        processData:false,

        success:function(res){

            try{

                let data = JSON.parse(res);

                if(data.success){

                    $('#preview').attr(
                        'src',
                        data.url + '?t=' + Date.now()
                    );

                    $('#uploadStatus').html(
                        '<span class="text-success">✓ Foto berhasil diperbarui</span>'
                    );

                }else{

                    $('#uploadStatus').html(
                        '<span class="text-danger">Upload gagal</span>'
                    );

                }

            }catch(e){

                $('#uploadStatus').html(
                    '<span class="text-danger">Terjadi kesalahan</span>'
                );

            }

        }

    });

});

</script>

3. Buat file upload_foto_siswa.php

<?php

include "koneksi.php";

$response = [
    'success' => false
];

if(
    isset($_FILES['foto']) &&
    isset($_POST['id_siswa'])
){

    $id_siswa = $_POST['id_siswa'];

    $folder = "uploads/siswa/";

    if(!is_dir($folder)){
        mkdir($folder,0777,true);
    }

    $ext = strtolower(
        pathinfo(
            $_FILES['foto']['name'],
            PATHINFO_EXTENSION
        )
    );

    $namaFile =
        "siswa_" .
        $id_siswa .
        "_" .
        time() .
        "." .
        $ext;

    $path = $folder . $namaFile;

    if(
        move_uploaded_file(
            $_FILES['foto']['tmp_name'],
            $path
        )
    ){

        mysqli_query(
            $conn,
            "UPDATE tbl_siswa
             SET gambar='$path'
             WHERE id='$id_siswa'"
        );

        $response['success'] = true;
        $response['url'] = $path;

    }

}

echo json_encode($response);

Bonus (lebih elegan)

Tambahkan CSS berikut:

.camera-btn{
    position:absolute;
    right:5px;
    bottom:5px;
    width:42px;
    height:42px;
    border:none;
    border-radius:50%;
    background:#0d6efd;
    color:#fff;
    box-shadow:0 4px 10px rgba(0,0,0,.2);
}

.profile-wrapper{
    position:relative;
    width:140px;
    margin:auto;
}

Hasilnya akan seperti WhatsApp/Instagram:

  • Klik ikon kamera 📷
  • Pilih foto
  • Preview langsung berubah
  • Upload otomatis ke server
  • Database tbl_siswa.gambar langsung terupdate
  • Tidak ada tombol upload lagi.

upload otomatis

Bisa. Cara yang umum dipakai di aplikasi modern adalah auto upload setelah pengguna memilih file, tanpa tombol "Upload".

Misalnya Anda punya input:

<input type="file" id="foto" accept="image/*">

Lalu ketika file dipilih, JavaScript langsung mengirimkannya ke server.

HTML

<form id="formFoto" enctype="multipart/form-data">

    <input type="file"
           id="foto"
           name="foto"
           accept="image/*"
           hidden>

</form>

<button class="btn btn-primary"
        onclick="document.getElementById('foto').click()">
    <i class="bi bi-camera"></i>
    Ganti Foto
</button>

Javascript Auto Upload

document.getElementById('foto')
.addEventListener('change', function(){

    let file = this.files[0];

    if(!file) return;

    let formData = new FormData();
    formData.append('foto', file);

    fetch('upload-foto.php', {
        method: 'POST',
        body: formData
    })
    .then(response => response.json())
    .then(data => {

        if(data.success){

            alert('Foto berhasil diupload');

            // Update preview foto
            document.getElementById('fotoProfil').src =
                data.url + '?t=' + Date.now();

        }

    });

});

PHP Upload

<?php

$response = [
    'success' => false
];

if(isset($_FILES['foto'])){

    $namaFile = time() . '_' . $_FILES['foto']['name'];

    move_uploaded_file(
        $_FILES['foto']['tmp_name'],
        'uploads/' . $namaFile
    );

    $response['success'] = true;
    $response['url'] = 'uploads/' . $namaFile;
}

echo json_encode($response);

Preview Sebelum Upload

Kalau ingin seperti Instagram atau WhatsApp:

document.getElementById('foto')
.addEventListener('change', function(){

    let file = this.files[0];

    if(file){

        let url = URL.createObjectURL(file);

        document.getElementById('fotoProfil').src = url;

        uploadFoto(file);

    }

});

Dengan cara ini:

  1. Klik foto profil 📷
  2. Pilih gambar dari galeri
  3. Preview langsung berubah
  4. File langsung terupload ke server
  5. Tidak perlu tombol Upload

Untuk Smartphone (Rekomendasi)

Jika ingin saat diklik langsung membuka kamera HP:

<input type="file"
       id="foto"
       accept="image/*"
       capture="environment"
       hidden>

atau

<input type="file"
       id="foto"
       accept="image/*"
       capture="user"
       hidden>
  • capture="user" → kamera depan (selfie)
  • capture="environment" → kamera belakang

Kombinasi yang sering dipakai aplikasi modern adalah:

<input type="file"
       id="foto"
       accept="image/*"
       hidden>

Lalu tampilkan modal:

📷 Ambil Foto
🖼️ Pilih dari Galeri
🗑️ Hapus Foto

Sehingga pengalaman pengguna terasa seperti WhatsApp atau Instagram.

upload gambar

Berikut contoh lengkap menggunakan Bootstrap 5 + PHP, dimana:

✅ Klik foto profil
✅ Pilih gambar dari galeri
✅ Preview langsung berubah
✅ File langsung terupload otomatis
✅ Tidak perlu tombol Upload
✅ Mobile Friendly


1. index.php

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Upload Foto Profil</title>

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">

<style>

body{
    background:#f5f7fb;
}

.profile-container{
    max-width:400px;
    margin:50px auto;
}

.profile-card{
    background:white;
    border-radius:20px;
    padding:30px;
    text-align:center;
    box-shadow:0 5px 20px rgba(0,0,0,.08);
}

.profile-wrapper{
    position:relative;
    width:150px;
    margin:auto;
}

.profile-image{
    width:150px;
    height:150px;
    border-radius:50%;
    object-fit:cover;
    border:4px solid #fff;
    box-shadow:0 5px 15px rgba(0,0,0,.15);
}

.camera-btn{
    position:absolute;
    right:0;
    bottom:5px;

    width:45px;
    height:45px;

    border:none;
    border-radius:50%;

    background:#0d6efd;
    color:white;

    display:flex;
    align-items:center;
    justify-content:center;

    box-shadow:0 3px 10px rgba(0,0,0,.2);
}

.upload-status{
    margin-top:15px;
    font-size:14px;
}

</style>
</head>
<body>

<div class="container">

    <div class="profile-container">

        <div class="profile-card">

            <h3 class="mb-4">
                Foto Profil
            </h3>

            <div class="profile-wrapper">

                <img
                    id="preview"
                    src="uploads/default.png"
                    class="profile-image">

                <button
                    class="camera-btn"
                    onclick="pilihFoto()">

                    <i class="bi bi-camera-fill"></i>

                </button>

            </div>

            <h4 class="mt-4">
                Evander
            </h4>

            <p class="text-muted">
                Bimbel SMP
            </p>

            <div
                id="status"
                class="upload-status text-secondary">

                Klik ikon kamera untuk mengganti foto

            </div>

        </div>

    </div>

</div>

<input
    type="file"
    id="foto"
    accept="image/*"
    hidden>

<script>

function pilihFoto()
{
    document.getElementById('foto').click();
}

document
.getElementById('foto')
.addEventListener('change', function(){

    let file = this.files[0];

    if(!file)
    {
        return;
    }

    // Preview langsung

    let previewUrl = URL.createObjectURL(file);

    document
        .getElementById('preview')
        .src = previewUrl;

    document
        .getElementById('status')
        .innerHTML = 'Mengupload foto...';

    // Upload otomatis

    let formData = new FormData();

    formData.append('foto', file);

    fetch('upload.php', {

        method:'POST',
        body:formData

    })
    .then(response => response.json())

    .then(data => {

        if(data.success)
        {
            document
                .getElementById('status')
                .innerHTML =
                '<span class="text-success">✓ Foto berhasil diupload</span>';

            document
                .getElementById('preview')
                .src =
                data.url + '?v=' + Date.now();
        }
        else
        {
            document
                .getElementById('status')
                .innerHTML =
                '<span class="text-danger">Upload gagal</span>';
        }

    })
    .catch(error => {

        document
            .getElementById('status')
            .innerHTML =
            '<span class="text-danger">Terjadi kesalahan</span>';

    });

});

</script>

</body>
</html>

2. upload.php

<?php

header('Content-Type: application/json');

$response = [
    'success' => false
];

if(isset($_FILES['foto']))
{
    $folder = 'uploads/';

    if(!is_dir($folder))
    {
        mkdir($folder, 0777, true);
    }

    $ext = pathinfo(
        $_FILES['foto']['name'],
        PATHINFO_EXTENSION
    );

    $namaFile =
        'foto_' .
        time() .
        '.' .
        $ext;

    $tujuan = $folder . $namaFile;

    if(
        move_uploaded_file(
            $_FILES['foto']['tmp_name'],
            $tujuan
        )
    )
    {
        $response['success'] = true;
        $response['url'] = $tujuan;
    }
}

echo json_encode($response);

Struktur Folder

project/
│
├── index.php
├── upload.php
│
└── uploads/
     └── default.png

Jika Ingin Langsung Membuka Kamera HP

Ganti:

<input
    type="file"
    id="foto"
    accept="image/*"
    hidden>

menjadi:

<input
    type="file"
    id="foto"
    accept="image/*"
    capture="user"
    hidden>

Maka pada sebagian besar smartphone Android:

📷 Klik ikon kamera → Kamera depan langsung terbuka → Ambil foto → Upload otomatis → Preview berubah otomatis.

Ini adalah pola yang mirip dengan aplikasi WhatsApp dan Instagram saat mengganti foto profil.

Sabtu, 06 Juni 2026

ambil gbar

Di browser, Anda tidak bisa langsung memaksa membuka kamera hanya dengan tag <a>. Yang umum dilakukan adalah menggunakan <input type="file"> dengan atribut:

accept="image/*"
capture="environment"

Kemudian saat menu "Ambil Foto" diklik, trigger input tersebut.

HTML

<a href="#"
   class="list-group-item list-group-item-action"
   onclick="document.getElementById('cameraInput').click(); return false;">
    
    <i class="bi bi-camera me-2"></i>
    Ambil Foto

</a>

<input type="file"
       id="cameraInput"
       accept="image/*"
       capture="environment"
       style="display:none;">

Preview Hasil Foto

Tambahkan elemen gambar:

<img id="previewFoto"
     src="<?=$row['gambar']?>"
     class="profile-photo">

Lalu JavaScript:

<script>
document.getElementById('cameraInput')
.addEventListener('change', function(e){

    const file = e.target.files[0];

    if(file){
        document.getElementById('previewFoto').src =
            URL.createObjectURL(file);
    }

});
</script>

Kamera Belakang atau Depan

Kamera belakang (default Android):

capture="environment"

Kamera depan (selfie):

capture="user"

Catatan Penting

Perilakunya tergantung perangkat:

  • Android Chrome → biasanya langsung muncul pilihan Kamera atau Galeri.
  • Beberapa Android → langsung membuka kamera.
  • iPhone Safari → biasanya menampilkan pilihan:
    • Take Photo
    • Photo Library

Ini normal dan tidak bisa dipaksa 100% oleh browser.


Jika Ingin Benar-Benar Mirip WhatsApp/Instagram

Gunakan API kamera browser:

navigator.mediaDevices.getUserMedia()

yang akan membuka kamera langsung di dalam modal Bootstrap dan menampilkan preview live. Setelah pengguna menekan tombol, foto diambil dan langsung di-upload. Ini terlihat jauh lebih profesional dan modern daripada input file biasa.

Untuk aplikasi bimbel Gardenia 1, saya merekomendasikan pendekatan kedua (kamera live dalam modal) karena pengalaman pengguna jauh lebih baik.

update foto

Untuk halaman update foto profil siswa, saya sarankan jangan hanya menampilkan foto bulat dan tombol upload biasa. Buatlah seperti pengalaman di Instagram, WhatsApp, atau Facebook agar terasa modern dan premium.

Contoh layout yang elegan:

┌────────────────────────────┐
│      Foto Profil           │
│                            │
│        ( FOTO )            │
│           📷               │
│                            │
│       Evander              │
│       Bimbel SMP           │
│                            │
│ [ Pilih Foto Baru ]        │
│                            │
│ Drag & Drop atau klik      │
│                            │
│  Preview Foto Baru         │
│                            │
│ [ Batal ] [ Simpan ]       │
└────────────────────────────┘

Desain ala Media Sosial

Foto profil diberi tombol kamera mengambang di kanan bawah.

<div class="profile-wrapper">
    
    <img src="foto.jpg" class="profile-image">

    <button class="camera-btn">
        <i class="bi bi-camera-fill"></i>
    </button>

</div>

CSS:

.profile-wrapper{
    position:relative;
    width:140px;
    margin:auto;
}

.profile-image{
    width:140px;
    height:140px;
    border-radius:50%;
    object-fit:cover;
    border:5px solid #fff;
    box-shadow:0 5px 20px rgba(0,0,0,.15);
}

.camera-btn{
    position:absolute;
    right:0;
    bottom:5px;
    width:45px;
    height:45px;
    border:none;
    border-radius:50%;
    background:#0d6efd;
    color:white;
    font-size:18px;
    box-shadow:0 4px 10px rgba(0,0,0,.2);
}

Hasilnya akan seperti:

       ○○○○○○○○
     ○           ○
    ○    FOTO     ○
     ○           ○
       ○○○○○○○○
             📷

Modal Upload Modern

Saat tombol kamera diklik:

<div class="modal fade" id="uploadFotoModal">

Isi modal:

┌────────────────────┐
│ Update Foto Profil │
├────────────────────┤
│  📷 Ambil Foto     │
│                    │
│  🖼️ Pilih Galeri   │
│                    │
│  🗑️ Hapus Foto     │
└────────────────────┘

Bootstrap:

<div class="list-group">

<a href="#" class="list-group-item list-group-item-action">
    <i class="bi bi-camera me-2"></i>
    Ambil Foto
</a>

<a href="#" class="list-group-item list-group-item-action">
    <i class="bi bi-image me-2"></i>
    Pilih dari Galeri
</a>

<a href="#" class="list-group-item list-group-item-action text-danger">
    <i class="bi bi-trash me-2"></i>
    Hapus Foto
</a>

</div>

Preview Sebelum Simpan

Seperti WhatsApp:

<input type="file"
       id="photoInput"
       accept="image/*"
       hidden>

<img id="preview"
     src="foto-default.jpg">

Javascript:

document
.getElementById('photoInput')
.addEventListener('change', function(e){

    const file = e.target.files[0];

    if(file){
        document.getElementById('preview').src =
            URL.createObjectURL(file);
    }

});

Level Premium (Rekomendasi)

Untuk aplikasi bimbel Gardenia 1, saya akan membuat:

  • Foto profil besar di tengah
  • Tombol kamera floating
  • Modal Bottom Sheet seperti aplikasi Android modern
  • Preview foto langsung
  • Crop foto sebelum upload
  • Kompres otomatis
  • Drag & Drop di desktop
  • Pilih Kamera/Galeri di smartphone
  • Animasi halus Bootstrap 5

Tampilannya akan sangat mirip pengalaman pengguna di Instagram dan WhatsApp, sehingga terlihat profesional dan modern.

admin

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Admin Dashboard - Bimbel Al Qur'an</title>

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">

<style>

body{
    background:#f8fafc;
    font-family:'Segoe UI',sans-serif;
}

.sidebar{
    width:280px;
    min-height:100vh;
    background:linear-gradient(
    180deg,
    #0f766e,
    #115e59);
    position:fixed;
    left:0;
    top:0;
    color:white;
}

.logo{
    padding:25px;
    text-align:center;
    border-bottom:1px solid rgba(255,255,255,.1);
}

.logo h4{
    margin:0;
    font-weight:700;
}

.menu a{
    display:block;
    color:white;
    text-decoration:none;
    padding:14px 25px;
    transition:.3s;
}

.menu a:hover{
    background:rgba(255,255,255,.1);
    padding-left:35px;
}

.menu i{
    width:25px;
}

.content{
    margin-left:280px;
    padding:30px;
}

.topbar{
    background:white;
    border-radius:20px;
    padding:15px 25px;
    box-shadow:0 10px 30px rgba(0,0,0,.05);
}

.stat-card{
    background:white;
    border:none;
    border-radius:24px;
    padding:25px;
    box-shadow:0 10px 25px rgba(0,0,0,.05);
    transition:.3s;
}

.stat-card:hover{
    transform:translateY(-5px);
}

.icon-box{
    width:60px;
    height:60px;
    border-radius:15px;
    display:flex;
    align-items:center;
    justify-content:center;
    font-size:24px;
    color:white;
}

.bg-green{
    background:#0f766e;
}

.bg-gold{
    background:#D4AF37;
}

.bg-blue{
    background:#2563eb;
}

.bg-red{
    background:#dc2626;
}

.card-premium{
    background:white;
    border:none;
    border-radius:24px;
    box-shadow:0 10px 30px rgba(0,0,0,.05);
}

.table thead{
    background:#f1f5f9;
}

.badge-status{
    background:#dcfce7;
    color:#166534;
    padding:8px 12px;
    border-radius:30px;
}

</style>
</head>
<body>

<!-- SIDEBAR -->

<div class="sidebar">

<div class="logo">
<h4>📖 Al Qur'an Academy</h4>
<small>Admin Panel</small>
</div>

<div class="menu mt-3">

<a href="#">
<i class="fas fa-home"></i>
Dashboard
</a>

<a href="#">
<i class="fas fa-user-graduate"></i>
Data Santri
</a>

<a href="#">
<i class="fas fa-chalkboard-teacher"></i>
Pengajar
</a>

<a href="#">
<i class="fas fa-video"></i>
Kelas Online
</a>

<a href="#">
<i class="fas fa-calendar"></i>
Jadwal
</a>

<a href="#">
<i class="fas fa-check-circle"></i>
Absensi
</a>

<a href="#">
<i class="fas fa-book-quran"></i>
Hafalan
</a>

<a href="#">
<i class="fas fa-wallet"></i>
Pembayaran
</a>

<a href="#">
<i class="fas fa-certificate"></i>
Sertifikat
</a>

<a href="#">
<i class="fas fa-chart-line"></i>
Laporan
</a>

<a href="#">
<i class="fas fa-gear"></i>
Pengaturan
</a>

</div>

</div>

<!-- CONTENT -->

<div class="content">

<!-- TOPBAR -->

<div class="topbar d-flex justify-content-between align-items-center">

<div>
<h4 class="mb-0 fw-bold">
Dashboard Admin
</h4>
<small class="text-muted">
Selamat Datang Kembali
</small>
</div>

<div class="d-flex align-items-center gap-3">

<button class="btn btn-light">
<i class="fas fa-bell"></i>
</button>

<img src="https://ui-avatars.com/api/?name=Admin"
width="45"
class="rounded-circle">

</div>

</div>

<!-- STATISTIK -->

<div class="row mt-4">

<div class="col-lg-3 mb-3">
<div class="stat-card">

<div class="d-flex justify-content-between">

<div>
<p class="text-muted mb-1">
Total Santri
</p>
<h3>1,254</h3>
</div>

<div class="icon-box bg-green">
<i class="fas fa-user-graduate"></i>
</div>

</div>

</div>
</div>

<div class="col-lg-3 mb-3">
<div class="stat-card">

<div class="d-flex justify-content-between">

<div>
<p class="text-muted mb-1">
Pengajar
</p>
<h3>36</h3>
</div>

<div class="icon-box bg-blue">
<i class="fas fa-chalkboard-teacher"></i>
</div>

</div>

</div>
</div>

<div class="col-lg-3 mb-3">
<div class="stat-card">

<div class="d-flex justify-content-between">

<div>
<p class="text-muted mb-1">
Kelas Aktif
</p>
<h3>72</h3>
</div>

<div class="icon-box bg-gold">
<i class="fas fa-video"></i>
</div>

</div>

</div>
</div>

<div class="col-lg-3 mb-3">
<div class="stat-card">

<div class="d-flex justify-content-between">

<div>
<p class="text-muted mb-1">
Pendapatan
</p>
<h3>Rp 24 Jt</h3>
</div>

<div class="icon-box bg-red">
<i class="fas fa-wallet"></i>
</div>

</div>

</div>
</div>

</div>

<!-- TABLE -->

<div class="card-premium p-4 mt-3">

<h5 class="fw-bold mb-4">
Santri Terbaru
</h5>

<div class="table-responsive">

<table class="table align-middle">

<thead>
<tr>
<th>Nama</th>
<th>Program</th>
<th>Pengajar</th>
<th>Status</th>
</tr>
</thead>

<tbody>

<tr>
<td>Ahmad Fauzi</td>
<td>Tahsin Dasar</td>
<td>Ust. Abdullah</td>
<td>
<span class="badge-status">
Aktif
</span>
</td>
</tr>

<tr>
<td>Fatimah Zahra</td>
<td>Tahfidz</td>
<td>Ustadzah Aisyah</td>
<td>
<span class="badge-status">
Aktif
</span>
</td>
</tr>

<tr>
<td>Muhammad Ali</td>
<td>Iqra'</td>
<td>Ust. Hasan</td>
<td>
<span class="badge-status">
Aktif
</span>
</td>
</tr>

</tbody>

</table>

</div>

</div>

</div>

</body>
</html>

Selasa, 02 Juni 2026

modal

Jika ikon plus tersebut ingin membuka modal Bootstrap 5 saat diklik, ubah kode ikon menjadi:

<i class="bi bi-plus-lg me-3"
   data-bs-toggle="modal"
   data-bs-target="#modalTambahSiswa"
   style="cursor:pointer"></i>

Kemudian tambahkan kode modal berikut sebelum tag </body>:

<!-- Modal Tambah Siswa -->
<div class="modal fade"
     id="modalTambahSiswa"
     tabindex="-1"
     aria-hidden="true">

    <div class="modal-dialog modal-dialog-centered">

        <div class="modal-content border-0 rounded-4">

            <div class="modal-header">
                <h5 class="modal-title fw-bold">
                    Tambah Siswa
                </h5>

                <button type="button"
                        class="btn-close"
                        data-bs-dismiss="modal">
                </button>
            </div>

            <div class="modal-body">

                <div class="mb-3">
                    <label class="form-label">
                        Nama Siswa
                    </label>

                    <input type="text"
                           class="form-control"
                           placeholder="Masukkan nama siswa">
                </div>

                <div class="mb-3">
                    <label class="form-label">
                        Nomor WhatsApp
                    </label>

                    <input type="text"
                           class="form-control"
                           placeholder="08xxxxxxxxxx">
                </div>

                <div class="mb-3">
                    <label class="form-label">
                        Program/Kelas
                    </label>

                    <select class="form-select">
                        <option>English For Kids</option>
                        <option>English For Teens</option>
                        <option>Bimbel SD</option>
                        <option>Bimbel SMP</option>
                    </select>
                </div>

            </div>

            <div class="modal-footer">

                <button type="button"
                        class="btn btn-light"
                        data-bs-dismiss="modal">
                    Batal
                </button>

                <button type="submit"
                        class="btn btn-primary">
                    Simpan
                </button>

            </div>

        </div>

    </div>

</div>

Jangan lupa memastikan file Bootstrap JS sudah dimuat:

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>

Jika aplikasi ini untuk manajemen bimbel, saya justru menyarankan modal yang lebih modern berisi menu pilihan seperti:

+ Tambah Siswa
+ Tambah Tagihan
+ Catat Pembayaran
+ Cetak Kartu SPP

sehingga satu tombol + dapat digunakan untuk berbagai aksi tanpa memenuhi tampilan halaman.

Minggu, 31 Mei 2026

template detail siswa

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Detail Siswa</title>

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">

<style>

body{
    background:#f5f6fa;
    font-family:'Segoe UI',sans-serif;
}

/* HEADER */
.topbar{
    background:#0d6efd;
    color:#fff;
    padding:20px;
    padding-top:40px;
    border-bottom-left-radius:25px;
    border-bottom-right-radius:25px;
}

.topbar h5{
    margin:0;
    font-weight:700;
}

/* PROFILE */
.profile-card{
    background:#fff;
    margin-top:-20px;
    border-radius:25px;
    padding:25px;
    box-shadow:0 3px 15px rgba(0,0,0,.05);
}

.profile-photo{
    width:120px;
    height:120px;
    border-radius:50%;
    object-fit:cover;
    border:4px solid #fff;
    box-shadow:0 3px 10px rgba(0,0,0,.1);
}

.student-name{
    font-size:24px;
    font-weight:700;
    margin-top:15px;
}

.student-program{
    color:#666;
}

.badge-paid{
    background:#e8f8ee;
    color:#1a9f4f;
    padding:10px 18px;
    border-radius:20px;
    font-weight:600;
}

.badge-unpaid{
    background:#ffeaea;
    color:#dc3545;
    padding:10px 18px;
    border-radius:20px;
    font-weight:600;
}

/* INFO CARD */
.info-card{
    background:#fff;
    border-radius:20px;
    padding:20px;
    margin-top:15px;
    box-shadow:0 3px 10px rgba(0,0,0,.04);
}

.info-label{
    color:#888;
    font-size:13px;
}

.info-value{
    font-size:16px;
    font-weight:600;
}

.icon-box{
    width:42px;
    height:42px;
    background:#eef4ff;
    border-radius:12px;
    display:flex;
    align-items:center;
    justify-content:center;
    color:#0d6efd;
    margin-right:12px;
}

.action-btn{
    border-radius:15px;
    padding:12px;
    font-weight:600;
}

</style>
</head>
<body>

<!-- HEADER -->
<div class="topbar">
    <div class="d-flex align-items-center justify-content-between">

        <div class="d-flex align-items-center">
            <i class="bi bi-arrow-left fs-4 me-3"></i>
            <h5>Detail Siswa</h5>
        </div>

        <i class="bi bi-three-dots-vertical fs-4"></i>

    </div>
</div>

<div class="container pb-5">

    <!-- PROFILE -->
    <div class="profile-card text-center">

        <img src="https://randomuser.me/api/portraits/boys/1.jpg"
             class="profile-photo">

        <div class="student-name">
            Ahmad Syarif
        </div>

        <div class="student-program">
            English For Kids - Level 1
        </div>

        <div class="mt-3">
            <span class="badge-unpaid">
                Belum Bayar
            </span>
        </div>

    </div>

    <!-- INFORMASI SISWA -->
    <div class="info-card">

        <h6 class="fw-bold mb-4">
            Informasi Siswa
        </h6>

        <div class="d-flex mb-3">
            <div class="icon-box">
                <i class="bi bi-person"></i>
            </div>

            <div>
                <div class="info-label">Nama Lengkap</div>
                <div class="info-value">Ahmad Syarif</div>
            </div>
        </div>

        <div class="d-flex mb-3">
            <div class="icon-box">
                <i class="bi bi-telephone"></i>
            </div>

            <div>
                <div class="info-label">Nomor WhatsApp</div>
                <div class="info-value">0812-1234-5678</div>
            </div>
        </div>

        <div class="d-flex mb-3">
            <div class="icon-box">
                <i class="bi bi-mortarboard"></i>
            </div>

            <div>
                <div class="info-label">Program</div>
                <div class="info-value">English For Kids Level 1</div>
            </div>
        </div>

        <div class="d-flex">
            <div class="icon-box">
                <i class="bi bi-people"></i>
            </div>

            <div>
                <div class="info-label">Nama Orang Tua</div>
                <div class="info-value">Bapak Rahman</div>
            </div>
        </div>

    </div>

    <!-- INFORMASI PEMBAYARAN -->
    <div class="info-card">

        <h6 class="fw-bold mb-4">
            Informasi Pembayaran
        </h6>

        <div class="row text-center">

            <div class="col-6">
                <div class="bg-light rounded-4 p-3">
                    <div class="text-muted small">
                        Tagihan Bulan Ini
                    </div>

                    <div class="fw-bold fs-5">
                        Rp300.000
                    </div>
                </div>
            </div>

            <div class="col-6">
                <div class="bg-light rounded-4 p-3">
                    <div class="text-muted small">
                        Status
                    </div>

                    <div class="fw-bold text-danger">
                        Belum Bayar
                    </div>
                </div>
            </div>

        </div>

    </div>

    <!-- ALAMAT -->
    <div class="info-card">

        <h6 class="fw-bold mb-3">
            Alamat
        </h6>

        <p class="mb-0 text-muted">
            Jl. Diponegoro No. 123,
            Kecamatan Pati,
            Kabupaten Pati,
            Jawa Tengah.
        </p>

    </div>

    <!-- TOMBOL -->
    <div class="row mt-4">

        <div class="col-6">
            <button class="btn btn-outline-primary w-100 action-btn">
                <i class="bi bi-pencil-square"></i>
                Edit
            </button>
        </div>

        <div class="col-6">
            <button class="btn btn-success w-100 action-btn">
                <i class="bi bi-whatsapp"></i>
                Hubungi
            </button>
        </div>

    </div>

</div>

</body>
</html>

Jumat, 29 Mei 2026

deployment:
  tasks:
    - export DEPLOYPATH=/home/zunuzmy1/public_html/
    - /bin/cp -R * $DEPLOYPATH

datatable

<link rel="stylesheet"
href="https://cdn.datatables.net/1.13.8/css/dataTables.bootstrap5.min.css">

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

<script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>

<script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap5.min.js"></script>


$(document).ready(function () {

  $('#tableSiswa').DataTable({
      responsive: true,
      pageLength: 10,
      lengthChange: false,
      searching: true,
      ordering: false,
      language: {
          search: "",
          paginate: {
              previous: "‹",
              next: "›"
          }
      }
  });

});

Senin, 25 Mei 2026

Minggu, 24 Mei 2026

css

/* =========================
   GLOBAL
========================= */

body{
  background:#f4f7fb;
  font-family:'Segoe UI',sans-serif;
  color:#1d1d1d;
}

a{
  text-decoration:none;
}

/* =========================
   HEADER / TOPBAR
========================= */

.topbar{
  background:linear-gradient(135deg,#0d6efd,#0052cc);
  padding:28px 22px 90px;
  border-bottom-left-radius:35px;
  border-bottom-right-radius:35px;
  position:relative;
  overflow:hidden;
}

.topbar::before{
  content:'';
  position:absolute;
  width:180px;
  height:180px;
  background:rgba(255,255,255,0.08);
  border-radius:50%;
  top:-50px;
  right:-40px;
}

.topbar::after{
  content:'';
  position:absolute;
  width:120px;
  height:120px;
  background:rgba(255,255,255,0.05);
  border-radius:50%;
  bottom:-40px;
  left:-30px;
}

.topbar h4{
  color:white;
  margin:0;
  font-weight:700;
}

.topbar i{
  color:white;
  font-size:28px;
}

/* =========================
   CARD
========================= */

.custom-card{
  background:white;
  border-radius:24px;
  padding:22px;
  box-shadow:0 5px 18px rgba(0,0,0,0.04);
  border:1px solid #eef1f5;
}

/* =========================
   INFO CARD
========================= */

.info-card{
  background:white;
  margin-top:-55px;
  border-radius:28px;
  padding:22px;
  box-shadow:0 10px 25px rgba(0,0,0,0.06);
  border:1px solid rgba(255,255,255,0.5);
  position:relative;
  z-index:10;
}

.balance-title{
  color:#212529;
  font-size:15px;
  font-weight:600;
}

.balance-amount{
  font-size:34px;
  font-weight:800;
  color:#0d6efd;
  margin-top:5px;
}

/* =========================
   BUTTON
========================= */

.btn-primary-custom{
  background:linear-gradient(135deg,#0d6efd,#0052cc);
  border:none;
  border-radius:18px;
  min-height:55px;
  font-size:18px;
  font-weight:700;
  color:white;
  width:100%;
  box-shadow:0 8px 20px rgba(13,110,253,0.25);
}

.btn-success-custom{
  background:#18c46c;
  border:none;
  border-radius:18px;
  min-height:55px;
  font-size:18px;
  font-weight:700;
  color:white;
  width:100%;
  box-shadow:0 8px 20px rgba(24,196,108,0.25);
}

/* =========================
   MENU CARD
========================= */

.menu-card{
  background:white;
  border-radius:24px;
  padding:22px 16px;
  text-align:center;
  box-shadow:0 5px 18px rgba(0,0,0,0.04);
  transition:0.2s;
  height:100%;
  border:1px solid #eef1f5;
}

.menu-card:hover{
  transform:translateY(-3px);
}

.menu-icon{
  width:65px;
  height:65px;
  border-radius:20px;
  display:flex;
  align-items:center;
  justify-content:center;
  margin:auto;
  margin-bottom:15px;
  font-size:30px;
}

.menu-title{
  font-size:17px;
  font-weight:700;
}

.menu-sub{
  color:#888;
  font-size:13px;
  margin-top:3px;
}

/* =========================
   FORM
========================= */

.form-control,
.form-select{
  border-radius:16px;
  min-height:56px;
  border:1px solid #e5e5e5;
  box-shadow:none !important;
}

/* =========================
   BOTTOM NAVIGATION
========================= */

.bottom-nav{
  position:fixed;
  bottom:0;
  left:0;
  right:0;
  background:white;
  border-top:1px solid #e9ecef;
  padding:10px 0;
  z-index:999;
  box-shadow:0 -3px 12px rgba(0,0,0,0.04);
}

.bottom-nav a{
  text-decoration:none;
  color:#999;
  display:flex;
  flex-direction:column;
  align-items:center;
  font-size:13px;
}

.bottom-nav a.active{
  color:#0d6efd;
  font-weight:700;
}

.bottom-nav i{
  font-size:24px;
  margin-bottom:4px;
}

/* =========================
   MOBILE
========================= */

@media(max-width:576px){

  .balance-amount{
    font-size:28px;
  }

  .menu-title{
    font-size:15px;
  }

  .menu-sub{
    font-size:12px;
  }

}

Sabtu, 23 Mei 2026

login

<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Login - Gardenia 1</title>

  <!-- Bootstrap 5 -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

  <!-- Bootstrap Icons -->
  <link rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">

  <style>

    body{
      background:#f5f6fa;
      font-family:'Segoe UI', sans-serif;
      min-height:100vh;
      overflow-x:hidden;
    }

    /* TOP BACKGROUND */
    .top-bg{
      background:linear-gradient(180deg,#0d6efd,#1677ff);
      height:320px;
      border-bottom-left-radius:45px;
      border-bottom-right-radius:45px;
      position:relative;
    }

    /* LOGO */
    .logo-box{
      width:95px;
      height:95px;
      background:white;
      border-radius:24px;
      display:flex;
      align-items:center;
      justify-content:center;
      box-shadow:0 8px 20px rgba(0,0,0,0.15);
      margin:auto;
    }

    .logo-box i{
      font-size:52px;
      color:#0d6efd;
    }

    /* LOGIN CARD */
    .login-card{
      background:white;
      border-radius:30px;
      padding:32px 24px;
      margin-top:-90px;
      box-shadow:0 8px 25px rgba(0,0,0,0.08);
    }

    .app-title{
      font-size:34px;
      font-weight:800;
      color:#111;
      margin-top:18px;
      margin-bottom:5px;
    }

    .app-subtitle{
      color:#777;
      font-size:17px;
      margin-bottom:28px;
    }

    /* FORM */
    .form-label{
      font-weight:700;
      color:#333;
      margin-bottom:10px;
    }

    .input-group{
      background:#f7f8fc;
      border-radius:18px;
      border:1px solid #e6e6e6;
      overflow:hidden;
      min-height:58px;
    }

    .input-group-text{
      background:transparent;
      border:none;
      color:#0d6efd;
      font-size:20px;
      padding-left:18px;
    }

    .form-control{
      border:none;
      background:transparent;
      box-shadow:none !important;
      font-size:17px;
    }

    .form-control::placeholder{
      color:#999;
    }

    /* LOGIN BUTTON */
    .btn-login{
      background:#0d6efd;
      border:none;
      width:100%;
      min-height:58px;
      border-radius:18px;
      color:white;
      font-size:20px;
      font-weight:700;
      box-shadow:0 8px 18px rgba(13,110,253,0.3);
      transition:0.2s;
    }

    .btn-login:hover{
      background:#0b5ed7;
      transform:translateY(-1px);
    }

    /* EXTRA */
    .forgot{
      text-decoration:none;
      color:#0d6efd;
      font-weight:600;
      font-size:15px;
    }

    .bottom-text{
      color:#888;
      font-size:15px;
      text-align:center;
      margin-top:28px;
    }

    .bottom-text a{
      color:#0d6efd;
      text-decoration:none;
      font-weight:700;
    }

    /* MOBILE */
    @media(max-width:576px){

      .top-bg{
        height:280px;
      }

      .app-title{
        font-size:28px;
      }

      .login-card{
        padding:28px 20px;
      }

    }

  </style>
</head>
<body>

  <!-- TOP BACKGROUND -->
  <div class="top-bg d-flex align-items-center justify-content-center">

    <div class="text-center text-white">

      <div class="logo-box">
        <i class="bi bi-mortarboard-fill"></i>
      </div>

      <h1 class="fw-bold mt-4 mb-2">
        Gardenia 1
      </h1>

      <div class="opacity-75">
        Aplikasi Kasir & Pembayaran
      </div>

    </div>

  </div>

  <!-- LOGIN CARD -->
  <div class="container"
       style="max-width:500px;">

    <div class="login-card">

      <div class="text-center">

        <div class="app-title">
          Selamat Datang
        </div>

        <div class="app-subtitle">
          Silakan login untuk melanjutkan
        </div>

      </div>

      <form>

        <!-- EMAIL -->
        <div class="mb-4">

          <label class="form-label">
            Email / Username
          </label>

          <div class="input-group">

            <span class="input-group-text">
              <i class="bi bi-person-fill"></i>
            </span>

            <input type="text"
                   class="form-control"
                   placeholder="Masukkan username">

          </div>

        </div>

        <!-- PASSWORD -->
        <div class="mb-3">

          <label class="form-label">
            Password
          </label>

          <div class="input-group">

            <span class="input-group-text">
              <i class="bi bi-lock-fill"></i>
            </span>

            <input type="password"
                   class="form-control"
                   placeholder="Masukkan password">

          </div>

        </div>

        <!-- REMEMBER -->
        <div class="d-flex justify-content-between align-items-center mb-4">

          <div class="form-check">

            <input class="form-check-input"
                   type="checkbox"
                   id="remember">

            <label class="form-check-label"
                   for="remember">
              Ingat saya
            </label>

          </div>

          <a href="#"
             class="forgot">
            Lupa Password?
          </a>

        </div>

        <!-- BUTTON -->
        <button type="submit"
                class="btn-login">

          <i class="bi bi-box-arrow-in-right me-2"></i>
          Masuk

        </button>

      </form>

      <!-- FOOTER -->
      <div class="bottom-text">

        Belum punya akun?
        <a href="#">
          Hubungi Admin
        </a>

      </div>

    </div>

  </div>

</body>
</html>

script 4

<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Kartu SPP</title>

  <!-- Bootstrap 5 -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

  <!-- Bootstrap Icons -->
  <link rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">

  <style>

    body{
      background:#f5f6fa;
      font-family:'Segoe UI', sans-serif;
      padding-bottom:100px;
    }

    /* HEADER */
    .topbar{
      background:#0d6efd;
      color:white;
      padding:22px 20px;
      padding-top:40px;
      border-bottom-left-radius:24px;
      border-bottom-right-radius:24px;
      box-shadow:0 3px 12px rgba(0,0,0,0.08);
    }

    .topbar a{
      color:white;
      text-decoration:none;
    }

    .topbar h4{
      margin:0;
      font-weight:700;
    }

    .topbar i{
      font-size:28px;
    }

    /* PROFILE */
    .profile-box{
      padding:24px 8px 10px;
    }

    .profile-img{
      width:95px;
      height:95px;
      border-radius:50%;
      object-fit:cover;
    }

    .student-name{
      font-size:22px;
      font-weight:700;
      margin-bottom:4px;
    }

    .student-class{
      color:#555;
      font-size:18px;
    }

    /* YEAR */
    .year-select{
      border-radius:16px;
      height:58px;
      font-size:20px;
      border:1px solid #e5e5e5;
      box-shadow:none !important;
    }

    /* MONTH CARD */
    .month-card{
      border-radius:20px;
      padding:22px 10px;
      text-align:center;
      background:white;
      border:1px solid #ededed;
      box-shadow:0 2px 8px rgba(0,0,0,0.03);
      height:100%;
    }

    .month-paid{
      background:#eefaf1;
    }

    .month-unpaid{
      background:#fff4f2;
    }

    .month-title{
      font-size:20px;
      font-weight:700;
      margin-bottom:12px;
    }

    .month-status{
      font-size:15px;
      font-weight:600;
    }

    .paid{
      color:#21a653;
    }

    .unpaid{
      color:#dc3545;
    }

    .normal{
      color:#666;
    }

    /* INFO BOX */
    .info-box{
      background:white;
      border-radius:24px;
      padding:22px;
      margin-top:28px;
      box-shadow:0 3px 12px rgba(0,0,0,0.05);
      border:1px solid #f0f0f0;
    }

    .info-title{
      font-size:22px;
      font-weight:700;
      margin-bottom:20px;
    }

    .info-row{
      display:flex;
      justify-content:space-between;
      margin-bottom:14px;
      font-size:18px;
    }

    .info-total{
      font-weight:700;
      font-size:20px;
      margin-top:10px;
    }

    /* BUTTON */
    .btn-custom{
      width:100%;
      border:none;
      border-radius:18px;
      min-height:58px;
      font-size:22px;
      font-weight:700;
      color:white;
    }

    .btn-pay{
      background:#18c46c;
      box-shadow:0 5px 14px rgba(24,196,108,0.3);
    }

    .btn-wa{
      background:#0d6efd;
      box-shadow:0 5px 14px rgba(13,110,253,0.3);
    }

    /* BOTTOM NAV */
    .bottom-nav{
      position:fixed;
      bottom:0;
      left:0;
      right:0;
      background:white;
      border-top:1px solid #e6e6e6;
      padding:10px 0;
      z-index:999;
    }

    .bottom-nav a{
      text-decoration:none;
      color:#8a8a8a;
      display:flex;
      flex-direction:column;
      align-items:center;
      font-size:13px;
    }

    .bottom-nav i{
      font-size:24px;
      margin-bottom:4px;
    }

    .bottom-nav .active{
      color:#0d6efd;
      font-weight:700;
    }

    @media(max-width:576px){

      .student-name{
        font-size:20px;
      }

      .student-class{
        font-size:15px;
      }

      .month-title{
        font-size:18px;
      }

      .month-status{
        font-size:14px;
      }

      .btn-custom{
        font-size:18px;
      }

      .info-row{
        font-size:16px;
      }

    }

  </style>
</head>
<body>

  <!-- HEADER -->
  <div class="topbar">

    <a href="#"
       class="d-flex align-items-center">

      <i class="bi bi-arrow-left me-3"></i>

      <h4>Kartu SPP</h4>

    </a>

  </div>

  <div class="container">

    <!-- PROFILE -->
    <div class="profile-box">

      <div class="d-flex align-items-center">

        <img src="https://randomuser.me/api/portraits/boys/1.jpg"
             class="profile-img me-3">

        <div>

          <div class="student-name">
            Ahmad Syarif
          </div>

          <div class="student-class">
            English for Kids - Level 1
          </div>

        </div>

      </div>

    </div>

    <!-- YEAR -->
    <div class="d-flex justify-content-between align-items-center mb-4 px-1">

      <h4 class="fw-bold mb-0">
        Tahun
      </h4>

      <div style="width:180px;">
        <select class="form-select year-select">
          <option>2026</option>
          <option>2025</option>
          <option>2024</option>
        </select>
      </div>

    </div>

    <!-- MONTH GRID -->
    <div class="row g-3">

      <div class="col-4">
        <div class="month-card month-paid">
          <div class="month-title">Jan</div>
          <div class="month-status paid">
            <i class="bi bi-check-circle-fill me-1"></i>
            Lunas
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card month-paid">
          <div class="month-title">Feb</div>
          <div class="month-status paid">
            <i class="bi bi-check-circle-fill me-1"></i>
            Lunas
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card month-paid">
          <div class="month-title">Mar</div>
          <div class="month-status paid">
            <i class="bi bi-check-circle-fill me-1"></i>
            Lunas
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card month-paid">
          <div class="month-title">Apr</div>
          <div class="month-status paid">
            <i class="bi bi-check-circle-fill me-1"></i>
            Lunas
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card month-unpaid">
          <div class="month-title">Mei</div>
          <div class="month-status unpaid">
            <i class="bi bi-x-circle me-1"></i>
            Belum Bayar
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card">
          <div class="month-title">Jun</div>
          <div class="month-status normal">
            Belum Bayar
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card">
          <div class="month-title">Jul</div>
          <div class="month-status normal">
            Belum Bayar
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card">
          <div class="month-title">Agu</div>
          <div class="month-status normal">
            Belum Bayar
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card">
          <div class="month-title">Sep</div>
          <div class="month-status normal">
            Belum Bayar
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card">
          <div class="month-title">Okt</div>
          <div class="month-status normal">
            Belum Bayar
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card">
          <div class="month-title">Nov</div>
          <div class="month-status normal">
            Belum Bayar
          </div>
        </div>
      </div>

      <div class="col-4">
        <div class="month-card">
          <div class="month-title">Des</div>
          <div class="month-status normal">
            Belum Bayar
          </div>
        </div>
      </div>

    </div>

    <!-- INFO -->
    <div class="info-box">

      <div class="info-title">
        Informasi Tagihan
      </div>

      <div class="info-row">
        <span>Jumlah Tagihan</span>
        <span>Rp300.000</span>
      </div>

      <div class="info-row">
        <span>Diskon</span>
        <span>Rp0</span>
      </div>

      <div class="info-row info-total">
        <span>Total</span>
        <span>Rp300.000</span>
      </div>

      <!-- BUTTON -->
      <div class="row mt-4 g-3">

        <div class="col-6">
          <button class="btn-custom btn-pay">
            Bayar Sekarang
          </button>
        </div>

        <div class="col-6">
          <button class="btn-custom btn-wa">
            <i class="bi bi-whatsapp me-2"></i>
            Kirim WA
          </button>
        </div>

      </div>

    </div>

  </div>

  <!-- BOTTOM NAV -->
  <div class="bottom-nav">

    <div class="container">

      <div class="row text-center">

        <div class="col">
          <a href="#">
            <i class="bi bi-house"></i>
            Beranda
          </a>
        </div>

        <div class="col">
          <a href="#">
            <i class="bi bi-people"></i>
            Siswa
          </a>
        </div>

        <div class="col">
          <a href="#">
            <i class="bi bi-file-earmark-text"></i>
            Tagihan
          </a>
        </div>

        <div class="col">
          <a href="#" class="active">
            <i class="bi bi-wallet2"></i>
            Pembayaran
          </a>
        </div>

        <div class="col">
          <a href="#">
            <i class="bi bi-list"></i>
            Lainnya
          </a>
        </div>

      </div>

    </div>

  </div>

</body>
</html>

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Press Release Distribution