Modul 06: Admin Manajemen Anggota & Sirkulasi Transaksi

Modul 06: Admin Manajemen Anggota & Sirkulasi Transaksi

Berikut adalah seluruh berkas kode program aktual yang digunakan dalam aplikasi SIPAS tanpa potongan atau placeholder. Kode dapat dipelajari dan diketikkan langsung pada Sublime Text 3.

1

Model Sirkulasi Transaksi (Transaksi_model.php)

Lokasi Berkas: application/models/Transaksi_model.php

application/models/Transaksi_model.php PHP
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * Transaksi_model
 * Model untuk menangani peminjaman, pengembalian, dan riwayat transaksi
 */
class Transaksi_model extends CI_Model {

    private $table = 'transaksi';

    public function __construct() {
        parent::__construct();
    }

    /**
     * Mengambil daftar transaksi dengan relasi users dan buku
     */
    public function get_all($limit = NULL, $offset = NULL, $status = NULL, $search = NULL) {
        $this->db->select('transaksi.*, users.nama as nama_siswa, users.nis, users.kelas, users.foto as foto_siswa, buku.kode_buku, buku.judul as judul_buku, buku.gambar as gambar_buku');
        $this->db->from($this->table);
        $this->db->join('users', 'users.id = transaksi.user_id', 'left');
        $this->db->join('buku', 'buku.id = transaksi.buku_id', 'left');

        if (!empty($status) && $status !== 'semua') {
            $this->db->where('transaksi.status', $status);
        }

        if (!empty($search)) {
            $this->db->group_start();
            $this->db->like('transaksi.kode_transaksi', $search);
            $this->db->or_like('users.nama', $search);
            $this->db->or_like('users.nis', $search);
            $this->db->or_like('buku.judul', $search);
            $this->db->group_end();
        }

        $this->db->order_by('transaksi.created_at', 'DESC');

        if ($limit !== NULL) {
            $this->db->limit($limit, $offset);
        }

        return $this->db->get()->result();
    }

    /**
     * Menghitung total data transaksi untuk pagination
     */
    public function count_all($status = NULL, $search = NULL) {
        $this->db->from($this->table);
        $this->db->join('users', 'users.id = transaksi.user_id', 'left');
        $this->db->join('buku', 'buku.id = transaksi.buku_id', 'left');

        if (!empty($status) && $status !== 'semua') {
            $this->db->where('transaksi.status', $status);
        }

        if (!empty($search)) {
            $this->db->group_start();
            $this->db->like('transaksi.kode_transaksi', $search);
            $this->db->or_like('users.nama', $search);
            $this->db->or_like('users.nis', $search);
            $this->db->or_like('buku.judul', $search);
            $this->db->group_end();
        }

        return $this->db->count_all_results();
    }

    /**
     * Mengambil detail transaksi lengkap berdasarkan ID
     */
    public function get_by_id($id) {
        $this->db->select('transaksi.*, users.nama as nama_siswa, users.nis, users.kelas, users.username, users.foto as foto_siswa, buku.kode_buku, buku.judul as judul_buku, buku.penulis, buku.penerbit, buku.rak, buku.gambar as gambar_buku');
        $this->db->from($this->table);
        $this->db->join('users', 'users.id = transaksi.user_id', 'left');
        $this->db->join('buku', 'buku.id = transaksi.buku_id', 'left');
        $this->db->where('transaksi.id', $id);
        return $this->db->get()->row();
    }

    /**
     * Mengambil transaksi berdasarkan user tertentu
     */
    public function get_by_user($user_id, $status = NULL, $search = NULL, $limit = NULL, $offset = NULL) {
        $this->db->select('transaksi.*, buku.kode_buku, buku.judul as judul_buku, buku.penulis, buku.penerbit, buku.rak, buku.kategori, buku.gambar as gambar_buku');
        $this->db->from($this->table);
        $this->db->join('buku', 'buku.id = transaksi.buku_id', 'left');
        $this->db->where('transaksi.user_id', $user_id);

        if (!empty($status) && $status !== 'semua') {
            $this->db->where('transaksi.status', $status);
        }

        if (!empty($search)) {
            $this->db->group_start();
            $this->db->like('buku.judul', $search);
            $this->db->or_like('transaksi.kode_transaksi', $search);
            $this->db->group_end();
        }

        $this->db->order_by('transaksi.created_at', 'DESC');

        if ($limit !== NULL) {
            $this->db->limit($limit, $offset);
        }

        return $this->db->get()->result();
    }

    /**
     * Hitung total transaksi milik user
     */
    public function count_by_user($user_id, $status = NULL, $search = NULL) {
        $this->db->from($this->table);
        $this->db->join('buku', 'buku.id = transaksi.buku_id', 'left');
        $this->db->where('transaksi.user_id', $user_id);

        if (!empty($status) && $status !== 'semua') {
            $this->db->where('transaksi.status', $status);
        }

        if (!empty($search)) {
            $this->db->group_start();
            $this->db->like('buku.judul', $search);
            $this->db->or_like('transaksi.kode_transaksi', $search);
            $this->db->group_end();
        }

        return $this->db->count_all_results();
    }

    /**
     * Mengambil transaksi aktif (dipinjam) oleh user
     */
    public function get_active_by_user($user_id) {
        return $this->get_by_user($user_id, 'dipinjam');
    }

    /**
     * Cek apakah user sedang meminjam buku yang sama dan belum dikembalikan
     */
    public function is_book_borrowed_by_user($user_id, $buku_id) {
        $this->db->where('user_id', $user_id);
        $this->db->where('buku_id', $buku_id);
        $this->db->where('status', 'dipinjam');
        return $this->db->count_all_results($this->table) > 0;
    }

    /**
     * Generate Kode Transaksi Unik: TRX + YYYYMMDD + 4 digit urut
     */
    public function generate_kode() {
        $date = date('Ymd');
        $prefix = 'TRX' . $date;
        $this->db->like('kode_transaksi', $prefix, 'after');
        $this->db->order_by('kode_transaksi', 'DESC');
        $last = $this->db->get($this->table, 1)->row();

        if ($last) {
            $last_num = (int) substr($last->kode_transaksi, -4);
            $next_num = str_pad($last_num + 1, 4, '0', STR_PAD_LEFT);
        } else {
            $next_num = '0001';
        }

        return $prefix . $next_num;
    }

    /**
     * Membuat Transaksi Peminjaman Baru dengan Database Transaction
     */
    public function create_peminjaman($user_id, $buku_id) {
        $this->db->trans_start();

        // 1. Ambil info buku & cek stok
        $buku = $this->db->get_where('buku', ['id' => $buku_id])->row();
        if (!$buku || $buku->stok <= 0) {
            $this->db->trans_rollback();
            return ['status' => false, 'message' => 'Buku sedang tidak tersedia atau stok habis.'];
        }

        // 2. Cek apakah user sedang meminjam buku yang sama
        if ($this->is_book_borrowed_by_user($user_id, $buku_id)) {
            $this->db->trans_rollback();
            return ['status' => false, 'message' => 'Anda masih meminjam buku ini. Harap kembalikan terlebih dahulu.'];
        }

        // 3. Generate kode transaksi & tanggal
        $kode_transaksi = $this->generate_kode();
        $tanggal_pinjam = date('Y-m-d');
        $batas_kembali = date('Y-m-d', strtotime('+7 days'));

        $data_transaksi = [
            'kode_transaksi' => $kode_transaksi,
            'user_id' => $user_id,
            'buku_id' => $buku_id,
            'tanggal_pinjam' => $tanggal_pinjam,
            'batas_kembali' => $batas_kembali,
            'tanggal_kembali' => NULL,
            'status' => 'dipinjam',
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s')
        ];

        $this->db->insert($this->table, $data_transaksi);

        // 4. Kurangi stok buku - 1
        $this->db->set('stok', 'stok - 1', FALSE);
        $this->db->where('id', $buku_id);
        $this->db->update('buku');

        $this->db->trans_complete();

        if ($this->db->trans_status() === FALSE) {
            return ['status' => false, 'message' => 'Gagal memproses transaksi peminjaman.'];
        }

        return ['status' => true, 'message' => 'Peminjaman berhasil dilakukan. Kode: ' . $kode_transaksi];
    }

    /**
     * Memproses Pengembalian Buku dengan Database Transaction
     */
    public function proses_pengembalian($transaksi_id, $allowed_user_id = NULL) {
        $this->db->trans_start();

        // 1. Ambil data transaksi
        $transaksi = $this->db->get_where($this->table, ['id' => $transaksi_id])->row();
        if (!$transaksi) {
            $this->db->trans_rollback();
            return ['status' => false, 'message' => 'Data transaksi tidak ditemukan.'];
        }

        // Proteksi kepemilikan transaksi jika dipanggil oleh role user
        if ($allowed_user_id !== NULL && $transaksi->user_id != $allowed_user_id) {
            $this->db->trans_rollback();
            return ['status' => false, 'message' => 'Anda tidak memiliki hak untuk mengembalikan transaksi ini.'];
        }

        // 2. Cek apakah sudah berstatus dikembalikan
        if ($transaksi->status === 'dikembalikan') {
            $this->db->trans_rollback();
            return ['status' => false, 'message' => 'Buku pada transaksi ini sudah pernah dikembalikan.'];
        }

        // 3. Update status transaksi
        $this->db->where('id', $transaksi_id);
        $this->db->update($this->table, [
            'status' => 'dikembalikan',
            'tanggal_kembali' => date('Y-m-d'),
            'updated_at' => date('Y-m-d H:i:s')
        ]);

        // 4. Kembalikan stok buku + 1
        $this->db->set('stok', 'stok + 1', FALSE);
        $this->db->where('id', $transaksi->buku_id);
        $this->db->update('buku');

        $this->db->trans_complete();

        if ($this->db->trans_status() === FALSE) {
            return ['status' => false, 'message' => 'Gagal memproses pengembalian buku.'];
        }

        return ['status' => true, 'message' => 'Buku berhasil dikembalikan. Terima kasih!'];
    }
}
๐Ÿ’ก Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
2

Controller Manajemen Anggota (admin/Anggota.php)

Lokasi Berkas: application/controllers/admin/Anggota.php

application/controllers/admin/Anggota.php PHP
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * Admin Anggota Controller
 * CRUD Data Anggota (Siswa)
 */
class Anggota extends CI_Controller {

    public function __construct() {
        parent::__construct();
        check_admin();
        $this->load->model('User_model');
        $this->load->model('Transaksi_model');
        $this->load->library('pagination');
    }

    /**
     * Daftar Anggota Siswa
     */
    public function index() {
        $search = $this->input->get('keyword', TRUE);
        $status = $this->input->get('status', TRUE);

        $limit = 10;
        $page = $this->input->get('page') ? (int) $this->input->get('page') : 1;
        $offset = ($page - 1) * $limit;

        $total_rows = $this->User_model->count_all('user', $search, $status);

        // Pagination setup
        $config['base_url'] = site_url('admin/anggota');
        $config['total_rows'] = $total_rows;
        $config['per_page'] = $limit;
        $config['page_query_string'] = TRUE;
        $config['query_string_segment'] = 'page';
        $config['cur_page'] = (string) $page;
        $config['reuse_query_string'] = TRUE;

        $config['full_tag_open'] = '<nav><ul class="pagination pagination-sm mb-0">';
        $config['full_tag_close'] = '</ul></nav>';
        $config['first_link'] = 'ยซ';
        $config['first_tag_open'] = '<li class="page-item">';
        $config['first_tag_close'] = '</li>';
        $config['last_link'] = 'ยป';
        $config['last_tag_open'] = '<li class="page-item">';
        $config['last_tag_close'] = '</li>';
        $config['next_link'] = 'โ€บ';
        $config['next_tag_open'] = '<li class="page-item">';
        $config['next_tag_close'] = '</li>';
        $config['prev_link'] = 'โ€น';
        $config['prev_tag_open'] = '<li class="page-item">';
        $config['prev_tag_close'] = '</li>';
        $config['cur_tag_open'] = '<li class="page-item active"><span class="page-link">';
        $config['cur_tag_close'] = '</span></li>';
        $config['num_tag_open'] = '<li class="page-item">';
        $config['num_tag_close'] = '</li>';
        $config['attributes'] = ['class' => 'page-link'];

        $this->pagination->initialize($config);

        $data['title'] = 'Data Anggota - SIPAS';
        $data['anggota'] = $this->User_model->search('user', $search, $status, $limit, $offset);
        $data['pagination'] = $this->pagination->create_links();
        $data['total_rows'] = $total_rows;
        $data['offset'] = $offset;
        $data['limit'] = $limit;
        $data['keyword'] = $search;
        $data['selected_status'] = $status;

        $this->load->view('admin/anggota/index', $data);
    }

    /**
     * Tambah Anggota Siswa
     */
    public function tambah() {
        $this->form_validation->set_rules('nama', 'Nama Lengkap', 'required|trim', [
            'required' => 'Nama lengkap wajib diisi.'
        ]);
        $this->form_validation->set_rules('nis', 'NIS', 'required|trim|is_unique[users.nis]', [
            'required' => 'NIS wajib diisi.',
            'is_unique' => 'NIS sudah digunakan oleh siswa lain.'
        ]);
        $this->form_validation->set_rules('kelas', 'Kelas', 'required|trim', [
            'required' => 'Kelas wajib diisi.'
        ]);
        $this->form_validation->set_rules('username', 'Username', 'required|trim|is_unique[users.username]|min_length[3]', [
            'required' => 'Username wajib diisi.',
            'is_unique' => 'Username sudah digunakan, silakan pilih yang lain.',
            'min_length' => 'Username minimal 3 karakter.'
        ]);
        $this->form_validation->set_rules('password', 'Password', 'required|min_length[5]', [
            'required' => 'Password wajib diisi.',
            'min_length' => 'Password minimal 5 karakter.'
        ]);
        $this->form_validation->set_rules('status', 'Status', 'required|in_list[aktif,nonaktif]');

        if ($this->form_validation->run() === FALSE) {
            $data['title'] = 'Tambah Anggota - SIPAS';
            $this->load->view('admin/anggota/tambah', $data);
        } else {
            $foto_filename = NULL;

            // Upload Foto Profil jika ada
            if (!empty($_FILES['foto']['name'])) {
                $upload_config['upload_path']   = './uploads/profile/';
                $upload_config['allowed_types'] = 'jpg|jpeg|png|webp';
                $upload_config['max_size']      = 2048; // 2MB
                $upload_config['encrypt_name']  = TRUE;

                $this->load->library('upload', $upload_config, 'profile_upload');
                if ($this->profile_upload->do_upload('foto')) {
                    $upload_res = $this->profile_upload->data();
                    $foto_filename = $upload_res['file_name'];
                } else {
                    $this->session->set_flashdata('error', 'Gagal upload foto: ' . $this->profile_upload->display_errors('', ''));
                    redirect('admin/anggota/tambah');
                    return;
                }
            }

            $insert_data = [
                'nama'       => $this->input->post('nama', TRUE),
                'nis'        => $this->input->post('nis', TRUE),
                'kelas'      => $this->input->post('kelas', TRUE),
                'foto'       => $foto_filename,
                'username'   => strtolower($this->input->post('username', TRUE)),
                'password'   => password_hash($this->input->post('password'), PASSWORD_BCRYPT),
                'role'       => 'user',
                'status'     => $this->input->post('status', TRUE),
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s')
            ];

            if ($this->User_model->create($insert_data)) {
                $this->session->set_flashdata('success', 'Data anggota berhasil ditambahkan.');
                redirect('admin/anggota');
            } else {
                $this->session->set_flashdata('error', 'Gagal menambahkan data anggota.');
                redirect('admin/anggota/tambah');
            }
        }
    }

    /**
     * Edit Anggota Siswa
     */
    public function edit($id) {
        $anggota = $this->User_model->get_by_id($id);
        if (!$anggota || $anggota->role !== 'user') {
            $this->session->set_flashdata('error', 'Data anggota tidak ditemukan.');
            redirect('admin/anggota');
        }

        $nis_rule = 'required|trim';
        if ($this->input->post('nis') !== $anggota->nis) {
            $nis_rule .= '|is_unique[users.nis]';
        }

        $username_rule = 'required|trim|min_length[3]';
        if ($this->input->post('username') !== $anggota->username) {
            $username_rule .= '|is_unique[users.username]';
        }

        $this->form_validation->set_rules('nama', 'Nama Lengkap', 'required|trim');
        $this->form_validation->set_rules('nis', 'NIS', $nis_rule);
        $this->form_validation->set_rules('kelas', 'Kelas', 'required|trim');
        $this->form_validation->set_rules('username', 'Username', $username_rule);
        $this->form_validation->set_rules('status', 'Status', 'required|in_list[aktif,nonaktif]');

        if (!empty($this->input->post('password'))) {
            $this->form_validation->set_rules('password', 'Password Baru', 'min_length[5]');
        }

        if ($this->form_validation->run() === FALSE) {
            $data['title'] = 'Edit Anggota - SIPAS';
            $data['anggota'] = $anggota;
            $this->load->view('admin/anggota/edit', $data);
        } else {
            $update_data = [
                'nama'       => $this->input->post('nama', TRUE),
                'nis'        => $this->input->post('nis', TRUE),
                'kelas'      => $this->input->post('kelas', TRUE),
                'username'   => strtolower($this->input->post('username', TRUE)),
                'status'     => $this->input->post('status', TRUE),
                'updated_at' => date('Y-m-d H:i:s')
            ];

            // Update password jika diisi
            if (!empty($this->input->post('password'))) {
                $update_data['password'] = password_hash($this->input->post('password'), PASSWORD_BCRYPT);
            }

            // Upload Foto Profil Baru jika ada
            if (!empty($_FILES['foto']['name'])) {
                $upload_config['upload_path']   = './uploads/profile/';
                $upload_config['allowed_types'] = 'jpg|jpeg|png|webp';
                $upload_config['max_size']      = 2048; // 2MB
                $upload_config['encrypt_name']  = TRUE;

                $this->load->library('upload', $upload_config, 'profile_upload');
                if ($this->profile_upload->do_upload('foto')) {
                    $upload_res = $this->profile_upload->data();
                    $update_data['foto'] = $upload_res['file_name'];

                    // Hapus foto lama jika ada
                    if (!empty($anggota->foto) && file_exists(FCPATH . 'uploads/profile/' . $anggota->foto)) {
                        @unlink(FCPATH . 'uploads/profile/' . $anggota->foto);
                    }
                } else {
                    $this->session->set_flashdata('error', 'Gagal upload foto: ' . $this->profile_upload->display_errors('', ''));
                    redirect('admin/anggota/edit/' . $id);
                    return;
                }
            }

            if ($this->User_model->update($id, $update_data)) {
                $this->session->set_flashdata('success', 'Data anggota berhasil diperbarui.');
                redirect('admin/anggota');
            } else {
                $this->session->set_flashdata('error', 'Gagal memperbarui data anggota.');
                redirect('admin/anggota/edit/' . $id);
            }
        }
    }

    /**
     * Detail Anggota beserta Aktivitas Peminjamannya
     */
    public function detail($id) {
        $anggota = $this->User_model->get_by_id($id);
        if (!$anggota || $anggota->role !== 'user') {
            $this->session->set_flashdata('error', 'Data anggota tidak ditemukan.');
            redirect('admin/anggota');
        }

        $data['title'] = 'Detail Anggota - ' . $anggota->nama;
        $data['anggota'] = $anggota;
        $data['transaksi'] = $this->Transaksi_model->get_by_user($id, NULL, NULL, 10, 0);

        $this->load->view('admin/anggota/detail', $data);
    }

    /**
     * Hapus Anggota Siswa
     */
    public function hapus($id) {
        $anggota = $this->User_model->get_by_id($id);
        if (!$anggota || $anggota->role !== 'user') {
            $this->session->set_flashdata('error', 'Data anggota tidak ditemukan.');
            redirect('admin/anggota');
        }

        // Cek apakah anggota masih memiliki pinjaman aktif
        if ($this->User_model->has_active_transaction($id)) {
            $this->session->set_flashdata('error', 'Anggota tidak dapat dihapus karena masih meminjam buku yang belum dikembalikan.');
            redirect('admin/anggota');
            return;
        }

        $foto = $anggota->foto;

        if ($this->User_model->delete($id)) {
            if (!empty($foto) && file_exists(FCPATH . 'uploads/profile/' . $foto)) {
                @unlink(FCPATH . 'uploads/profile/' . $foto);
            }
            $this->session->set_flashdata('success', 'Data anggota berhasil dihapus.');
        } else {
            $this->session->set_flashdata('error', 'Gagal menghapus anggota.');
        }
        redirect('admin/anggota');
    }
}
๐Ÿ’ก Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
3

Controller Transaksi Sirkulasi (admin/Transaksi.php)

Lokasi Berkas: application/controllers/admin/Transaksi.php

application/controllers/admin/Transaksi.php PHP
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * Admin Transaksi Controller
 * Monitoring transaksi, filter status, dan proses pengembalian buku
 */
class Transaksi extends CI_Controller {

    public function __construct() {
        parent::__construct();
        check_admin();
        $this->load->model('Transaksi_model');
        $this->load->library('pagination');
    }

    /**
     * Menampilkan daftar transaksi dengan filter & pencarian
     */
    public function index() {
        $search = $this->input->get('keyword', TRUE);
        $status = $this->input->get('status', TRUE);

        $limit = 10;
        $page = $this->input->get('page') ? (int) $this->input->get('page') : 1;
        $offset = ($page - 1) * $limit;

        $total_rows = $this->Transaksi_model->count_all($status, $search);

        // Pagination setup
        $config['base_url'] = site_url('admin/transaksi');
        $config['total_rows'] = $total_rows;
        $config['per_page'] = $limit;
        $config['page_query_string'] = TRUE;
        $config['query_string_segment'] = 'page';
        $config['cur_page'] = (string) $page;
        $config['reuse_query_string'] = TRUE;

        $config['full_tag_open'] = '<nav><ul class="pagination pagination-sm mb-0">';
        $config['full_tag_close'] = '</ul></nav>';
        $config['first_link'] = 'ยซ';
        $config['first_tag_open'] = '<li class="page-item">';
        $config['first_tag_close'] = '</li>';
        $config['last_link'] = 'ยป';
        $config['last_tag_open'] = '<li class="page-item">';
        $config['last_tag_close'] = '</li>';
        $config['next_link'] = 'โ€บ';
        $config['next_tag_open'] = '<li class="page-item">';
        $config['next_tag_close'] = '</li>';
        $config['prev_link'] = 'โ€น';
        $config['prev_tag_open'] = '<li class="page-item">';
        $config['prev_tag_close'] = '</li>';
        $config['cur_tag_open'] = '<li class="page-item active"><span class="page-link">';
        $config['cur_tag_close'] = '</span></li>';
        $config['num_tag_open'] = '<li class="page-item">';
        $config['num_tag_close'] = '</li>';
        $config['attributes'] = ['class' => 'page-link'];

        $this->pagination->initialize($config);

        $data['title'] = 'Data Transaksi - SIPAS';
        $data['transaksi'] = $this->Transaksi_model->get_all($limit, $offset, $status, $search);
        $data['pagination'] = $this->pagination->create_links();
        $data['total_rows'] = $total_rows;
        $data['offset'] = $offset;
        $data['limit'] = $limit;
        $data['keyword'] = $search;
        $data['selected_status'] = $status ? $status : 'semua';

        $this->load->view('admin/transaksi/index', $data);
    }

    /**
     * Detail Transaksi
     */
    public function detail($id) {
        $transaksi = $this->Transaksi_model->get_by_id($id);
        if (!$transaksi) {
            $this->session->set_flashdata('error', 'Data transaksi tidak ditemukan.');
            redirect('admin/transaksi');
        }

        $data['title'] = 'Detail Transaksi ' . $transaksi->kode_transaksi;
        $data['transaksi'] = $transaksi;
        $this->load->view('admin/transaksi/detail', $data);
    }

    /**
     * Admin memproses pengembalian buku siswa
     */
    public function proses_kembali($id) {
        $result = $this->Transaksi_model->proses_pengembalian($id);

        if ($result['status']) {
            $this->session->set_flashdata('success', $result['message']);
        } else {
            $this->session->set_flashdata('error', $result['message']);
        }

        redirect('admin/transaksi/detail/' . $id);
    }
}
๐Ÿ’ก Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
4

View Daftar Anggota Siswa (admin/anggota/index.php)

Lokasi Berkas: application/views/admin/anggota/index.php

Tampilan Data Anggota
Gambar: Pratinjau tabel manajemen akun anggota siswa dengan filter status dan menu aksi (/admin/anggota).
application/views/admin/anggota/index.php PHP
<?php $this->load->view('layouts/header'); ?>

<div class="app-wrapper">
    <?php $this->load->view('layouts/sidebar_admin'); ?>

    <div class="app-main">
        <?php $this->load->view('layouts/navbar'); ?>

        <main class="app-content">
            <div class="page-header">
                <div>
                    <h1 class="page-title">Data Anggota</h1>
                    <p class="page-subtitle">Kelola siswa yang terdaftar sebagai anggota perpustakaan</p>
                </div>
                <div>
                    <a href="<?= site_url('admin/anggota/tambah') ?>" class="btn btn-primary">
                        <i class="bi bi-plus-lg"></i> Tambah Anggota
                    </a>
                </div>
            </div>

            <?php $this->load->view('layouts/flash_message'); ?>

            <!-- Search & Filter Card -->
            <div class="card border-0 mb-3">
                <div class="card-body p-3">
                    <form action="<?= site_url('admin/anggota') ?>" method="GET" class="row g-2 align-items-center">
                        <div class="col-md-7 col-lg-8">
                            <div class="search-input-group">
                                <i class="bi bi-search"></i>
                                <input type="text" class="form-control" name="keyword" value="<?= html_escape($keyword) ?>" placeholder="Cari nama, NIS atau username...">
                            </div>
                        </div>
                        <div class="col-md-3 col-lg-3">
                            <select name="status" class="form-select" onchange="this.form.submit()">
                                <option value="">Semua Status</option>
                                <option value="aktif" <?= $selected_status === 'aktif' ? 'selected' : '' ?>>Aktif</option>
                                <option value="nonaktif" <?= $selected_status === 'nonaktif' ? 'selected' : '' ?>>Nonaktif</option>
                            </select>
                        </div>
                        <div class="col-md-2 col-lg-1 d-flex gap-1">
                            <button type="submit" class="btn btn-primary w-100 justify-content-center">
                                Cari
                            </button>
                            <?php if (!empty($keyword) || !empty($selected_status)): ?>
                                <a href="<?= site_url('admin/anggota') ?>" class="btn btn-light" title="Reset filter">
                                    <i class="bi bi-x-lg"></i>
                                </a>
                            <?php endif; ?>
                        </div>
                    </form>
                </div>
            </div>

            <!-- Table Card -->
            <div class="card border-0">
                <div class="table-responsive">
                    <table class="table table-hover align-middle mb-0">
                        <thead>
                            <tr>
                                <th style="width: 50px;">NO</th>
                                <th>NIS</th>
                                <th>NAMA SISWA</th>
                                <th>KELAS</th>
                                <th>USERNAME</th>
                                <th>STATUS</th>
                                <th style="width: 70px;" class="text-center">AKSI</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php if (empty($anggota)): ?>
                                <tr>
                                    <td colspan="7">
                                        <div class="empty-state">
                                            <div class="empty-state-icon"><i class="bi bi-people"></i></div>
                                            <h6 class="fw-bold text-dark">Belum ada data anggota siswa</h6>
                                            <p class="text-secondary small mb-3">Tambahkan akun anggota untuk siswa sekolah.</p>
                                            <a href="<?= site_url('admin/anggota/tambah') ?>" class="btn btn-primary btn-sm">
                                                <i class="bi bi-plus-lg"></i> Tambah Anggota Baru
                                            </a>
                                        </div>
                                    </td>
                                </tr>
                            <?php else: ?>
                                <?php $no = $offset + 1; foreach ($anggota as $item): ?>
                                    <tr>
                                        <td><?= $no++ ?></td>
                                        <td class="fw-semibold text-dark"><?= html_escape($item->nis ? $item->nis : 'โ€”') ?></td>
                                        <td>
                                            <div class="d-flex align-items-center gap-2">
                                                <?= get_user_avatar_html($item->foto, $item->nama, 34) ?>
                                                <div>
                                                    <div class="fw-semibold text-dark"><?= html_escape($item->nama) ?></div>
                                                    <div class="text-muted small">Terdaftar sejak: <?= format_tanggal_indo(substr($item->created_at, 0, 10)) ?></div>
                                                </div>
                                            </div>
                                        </td>
                                        <td><?= html_escape($item->kelas ? $item->kelas : 'โ€”') ?></td>
                                        <td><?= html_escape($item->username) ?></td>
                                        <td>
                                            <?php if ($item->status === 'aktif'): ?>
                                                <span class="badge badge-soft-success"><i class="bi bi-check-circle-fill me-1"></i> Aktif</span>
                                            <?php else: ?>
                                                <span class="badge badge-soft-danger"><i class="bi bi-dash-circle-fill me-1"></i> Nonaktif</span>
                                            <?php endif; ?>
                                        </td>
                                        <td class="text-center">
                                            <div class="dropdown">
                                                <button class="btn btn-light btn-sm p-1 border-0" type="button" data-bs-toggle="dropdown" aria-expanded="false">
                                                    <i class="bi bi-three-dots-vertical"></i>
                                                </button>
                                                <ul class="dropdown-menu dropdown-menu-end shadow-sm">
                                                    <li>
                                                        <a class="dropdown-item py-2 small" href="<?= site_url('admin/anggota/detail/' . $item->id) ?>">
                                                            <i class="bi bi-eye text-info me-2"></i> Detail & Peminjaman
                                                        </a>
                                                    </li>
                                                    <li>
                                                        <a class="dropdown-item py-2 small" href="<?= site_url('admin/anggota/edit/' . $item->id) ?>">
                                                            <i class="bi bi-pencil text-warning me-2"></i> Edit Data
                                                        </a>
                                                    </li>
                                                    <li><hr class="dropdown-divider"></li>
                                                    <li>
                                                        <button type="button" class="dropdown-item py-2 small text-danger" data-bs-toggle="modal" data-bs-target="#deleteMemberModal<?= $item->id ?>">
                                                            <i class="bi bi-trash me-2"></i> Hapus
                                                        </button>
                                                    </li>
                                                </ul>
                                            </div>

                                            <!-- Modal Konfirmasi Hapus Anggota -->
                                            <div class="modal fade text-start" id="deleteMemberModal<?= $item->id ?>" tabindex="-1" aria-hidden="true">
                                                <div class="modal-dialog modal-dialog-centered" style="max-width: 400px;">
                                                    <div class="modal-content border-0 shadow">
                                                        <div class="modal-header border-bottom-0 pb-0">
                                                            <h6 class="modal-title fw-bold text-dark">Hapus Anggota?</h6>
                                                            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                                        </div>
                                                        <div class="modal-body py-3">
                                                            <p class="mb-1 text-secondary small">
                                                                Anggota "<strong><?= html_escape($item->nama) ?></strong>" akan dihapus dari sistem.
                                                            </p>
                                                            <p class="text-danger small mb-0">Pastikan siswa tidak memiliki pinjaman buku yang belum dikembalikan.</p>
                                                        </div>
                                                        <div class="modal-footer border-top-0 pt-0">
                                                            <button type="button" class="btn btn-light btn-sm" data-bs-dismiss="modal">Batal</button>
                                                            <a href="<?= site_url('admin/anggota/hapus/' . $item->id) ?>" class="btn btn-danger btn-sm">
                                                                Hapus Anggota
                                                            </a>
                                                        </div>
                                                    </div>
                                                </div>
                                            </div>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                        </tbody>
                    </table>
                </div>

                <!-- Footer pagination -->
                <?php if ($total_rows > 0): ?>
                    <div class="card-footer bg-white d-flex justify-content-between align-items-center flex-wrap gap-2 py-3">
                        <span class="text-secondary small">
                            Menampilkan <?= min($offset + 1, $total_rows) ?>โ€“<?= min($offset + $limit, $total_rows) ?> dari <?= $total_rows ?> anggota
                        </span>
                        <div>
                            <?= $pagination ?>
                        </div>
                    </div>
                <?php endif; ?>
            </div>
        </main>
    </div>
</div>

<?php $this->load->view('layouts/footer'); ?>
5

View Sirkulasi Transaksi (admin/transaksi/index.php)

Lokasi Berkas: application/views/admin/transaksi/index.php

Tampilan Transaksi Sirkulasi
Gambar: Pratinjau antarmuka sirkulasi peminjaman dengan filter status transaksi (/admin/transaksi).
application/views/admin/transaksi/index.php PHP
<?php $this->load->view('layouts/header'); ?>

<div class="app-wrapper">
    <?php $this->load->view('layouts/sidebar_admin'); ?>

    <div class="app-main">
        <?php $this->load->view('layouts/navbar'); ?>

        <main class="app-content">
            <div class="page-header">
                <div>
                    <h1 class="page-title">Transaksi Peminjaman</h1>
                    <p class="page-subtitle">Pantau seluruh aktivitas sirkulasi peminjaman dan pengembalian buku</p>
                </div>
            </div>

            <?php $this->load->view('layouts/flash_message'); ?>

            <!-- Filter & Search Toolbar -->
            <div class="card border-0 mb-3">
                <div class="card-body p-3">
                    <form action="<?= site_url('admin/transaksi') ?>" method="GET" class="row g-2 align-items-center">
                        <div class="col-md-7 col-lg-8">
                            <div class="search-input-group">
                                <i class="bi bi-search"></i>
                                <input type="text" class="form-control" name="keyword" value="<?= html_escape($keyword) ?>" placeholder="Cari kode transaksi, nama siswa, NIS atau judul buku...">
                            </div>
                        </div>
                        <div class="col-md-5 col-lg-4 d-flex gap-2">
                            <!-- Status Button Group Filter -->
                            <div class="btn-group w-100" role="group">
                                <a href="<?= site_url('admin/transaksi?status=semua' . ($keyword ? '&keyword=' . urlencode($keyword) : '')) ?>" class="btn btn-sm <?= ($selected_status === 'semua' || empty($selected_status)) ? 'btn-primary' : 'btn-light' ?>">
                                    Semua
                                </a>
                                <a href="<?= site_url('admin/transaksi?status=dipinjam' . ($keyword ? '&keyword=' . urlencode($keyword) : '')) ?>" class="btn btn-sm <?= $selected_status === 'dipinjam' ? 'btn-primary' : 'btn-light' ?>">
                                    Dipinjam
                                </a>
                                <a href="<?= site_url('admin/transaksi?status=dikembalikan' . ($keyword ? '&keyword=' . urlencode($keyword) : '')) ?>" class="btn btn-sm <?= $selected_status === 'dikembalikan' ? 'btn-primary' : 'btn-light' ?>">
                                    Kembali
                                </a>
                            </div>

                            <button type="submit" class="btn btn-primary btn-sm">Cari</button>
                            <?php if (!empty($keyword) || (!empty($selected_status) && $selected_status !== 'semua')): ?>
                                <a href="<?= site_url('admin/transaksi') ?>" class="btn btn-light btn-sm" title="Reset">
                                    <i class="bi bi-x-lg"></i>
                                </a>
                            <?php endif; ?>
                        </div>
                    </form>
                </div>
            </div>

            <!-- Table Card -->
            <div class="card border-0">
                <div class="table-responsive">
                    <table class="table table-hover align-middle mb-0">
                        <thead>
                            <tr>
                                <th>KODE TRX</th>
                                <th>NAMA SISWA</th>
                                <th>JUDUL BUKU</th>
                                <th>TGL PINJAM</th>
                                <th>BATAS KEMBALI</th>
                                <th>TGL KEMBALI</th>
                                <th>STATUS</th>
                                <th style="width: 70px;" class="text-center">AKSI</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php if (empty($transaksi)): ?>
                                <tr>
                                    <td colspan="8">
                                        <div class="empty-state">
                                            <div class="empty-state-icon"><i class="bi bi-arrow-left-right"></i></div>
                                            <h6 class="fw-bold text-dark">Tidak ada data transaksi</h6>
                                            <p class="text-secondary small mb-0">Belum ada transaksi yang sesuai dengan filter atau kata kunci Anda.</p>
                                        </div>
                                    </td>
                                </tr>
                            <?php else: ?>
                                <?php foreach ($transaksi as $item): ?>
                                    <tr>
                                        <td class="fw-semibold text-dark"><?= html_escape($item->kode_transaksi) ?></td>
                                        <td>
                                            <div class="fw-semibold text-dark"><?= html_escape($item->nama_siswa) ?></div>
                                            <div class="text-muted small">NIS: <?= html_escape($item->nis ? $item->nis : 'โ€”') ?> (<?= html_escape($item->kelas ? $item->kelas : 'โ€”') ?>)</div>
                                        </td>
                                        <td>
                                            <div class="fw-semibold text-dark"><?= html_escape($item->judul_buku) ?></div>
                                            <div class="text-muted small">Kode: <?= html_escape($item->kode_buku) ?></div>
                                        </td>
                                        <td><?= format_tanggal_indo($item->tanggal_pinjam) ?></td>
                                        <td>
                                            <span class="<?= (strtotime($item->batas_kembali) < strtotime(date('Y-m-d')) && $item->status === 'dipinjam') ? 'text-danger fw-bold' : '' ?>">
                                                <?= format_tanggal_indo($item->batas_kembali) ?>
                                            </span>
                                        </td>
                                        <td><?= format_tanggal_indo($item->tanggal_kembali) ?></td>
                                        <td>
                                            <?php if ($item->status === 'dipinjam'): ?>
                                                <span class="badge badge-soft-warning"><i class="bi bi-clock-history me-1"></i> Dipinjam</span>
                                            <?php else: ?>
                                                <span class="badge badge-soft-success"><i class="bi bi-check-circle me-1"></i> Dikembalikan</span>
                                            <?php endif; ?>
                                        </td>
                                        <td class="text-center">
                                            <a href="<?= site_url('admin/transaksi/detail/' . $item->id) ?>" class="btn btn-light btn-sm py-1 px-2" title="Detail Transaksi">
                                                <i class="bi bi-eye text-primary"></i>
                                            </a>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                        </tbody>
                    </table>
                </div>

                <!-- Footer pagination -->
                <?php if ($total_rows > 0): ?>
                    <div class="card-footer bg-white d-flex justify-content-between align-items-center flex-wrap gap-2 py-3">
                        <span class="text-secondary small">
                            Menampilkan <?= min($offset + 1, $total_rows) ?>โ€“<?= min($offset + $limit, $total_rows) ?> dari <?= $total_rows ?> transaksi
                        </span>
                        <div>
                            <?= $pagination ?>
                        </div>
                    </div>
                <?php endif; ?>
            </div>
        </main>
    </div>
</div>

<?php $this->load->view('layouts/footer'); ?>
6

View Formulir Tambah Anggota (admin/anggota/tambah.php)

Lokasi Berkas: application/views/admin/anggota/tambah.php

Tampilan Tambah Anggota
Gambar: Pratinjau antarmuka pendaftaran akun anggota siswa baru oleh Admin (/admin/anggota/tambah).
application/views/admin/anggota/tambah.php PHP
<?php $this->load->view('layouts/header'); ?>

<div class="app-wrapper">
    <?php $this->load->view('layouts/sidebar_admin'); ?>

    <div class="app-main">
        <?php $this->load->view('layouts/navbar'); ?>

        <main class="app-content">
            <div class="mb-3">
                <a href="<?= site_url('admin/anggota') ?>" class="text-decoration-none text-secondary small fw-semibold">
                    <i class="bi bi-chevron-left"></i> Kembali ke Data Anggota
                </a>
            </div>

            <div class="page-header">
                <div>
                    <h1 class="page-title">Tambah Anggota</h1>
                    <p class="page-subtitle">Buat akun siswa untuk mengakses layanan perpustakaan</p>
                </div>
            </div>

            <?php $this->load->view('layouts/flash_message'); ?>

            <div class="card border-0 form-page">
                <div class="card-body p-4">
                    <?= form_open_multipart('admin/anggota/tambah') ?>
                        <div class="mb-3">
                            <label for="nama" class="form-label">Nama Lengkap Siswa <span class="text-danger">*</span></label>
                            <input type="text" class="form-control" id="nama" name="nama" value="<?= set_value('nama') ?>" placeholder="Contoh: Andi Pratama" required autofocus>
                        </div>

                        <div class="row g-3 mb-3">
                            <div class="col-md-6">
                                <label for="nis" class="form-label">NIS (Nomor Induk Siswa) <span class="text-danger">*</span></label>
                                <input type="text" class="form-control" id="nis" name="nis" value="<?= set_value('nis') ?>" placeholder="Contoh: 23001" required>
                            </div>
                            <div class="col-md-6">
                                <label for="kelas" class="form-label">Kelas <span class="text-danger">*</span></label>
                                <input type="text" class="form-control" id="kelas" name="kelas" value="<?= set_value('kelas') ?>" placeholder="Contoh: XII RPL 1" required>
                            </div>
                        </div>

                        <div class="row g-3 mb-3">
                            <div class="col-md-6">
                                <label for="username" class="form-label">Username Login <span class="text-danger">*</span></label>
                                <input type="text" class="form-control" id="username" name="username" value="<?= set_value('username') ?>" placeholder="Pilih username" required>
                            </div>
                            <div class="col-md-6">
                                <label for="password" class="form-label">Password <span class="text-danger">*</span></label>
                                <input type="password" class="form-control" id="password" name="password" placeholder="Minimal 5 karakter" required>
                            </div>
                        </div>

                        <div class="mb-3">
                            <label for="foto" class="form-label">Foto Profil Siswa (Opsional)</label>
                            <input type="file" class="form-control" id="foto" name="foto" accept="image/png, image/jpeg, image/webp">
                            <div class="form-text">Format: JPG, PNG, WEBP. Maksimal 2MB.</div>
                        </div>

                        <div class="mb-4">
                            <label class="form-label d-block">Status Akun <span class="text-danger">*</span></label>
                            <div class="form-check form-check-inline">
                                <input class="form-check-input" type="radio" name="status" id="statusAktif" value="aktif" <?= set_radio('status', 'aktif', TRUE) ?>>
                                <label class="form-check-label" for="statusAktif">Aktif</label>
                            </div>
                            <div class="form-check form-check-inline">
                                <input class="form-check-input" type="radio" name="status" id="statusNonaktif" value="nonaktif" <?= set_radio('status', 'nonaktif') ?>>
                                <label class="form-check-label" for="statusNonaktif">Nonaktif</label>
                            </div>
                        </div>

                        <div class="d-flex gap-2">
                            <button type="submit" class="btn btn-primary px-4">
                                <i class="bi bi-check-lg"></i> Simpan Anggota
                            </button>
                            <a href="<?= site_url('admin/anggota') ?>" class="btn btn-light px-4">Batal</a>
                        </div>
                    </form>
                </div>
            </div>
        </main>
    </div>
</div>

<?php $this->load->view('layouts/footer'); ?>
7

View Formulir Edit Anggota (admin/anggota/edit.php)

Lokasi Berkas: application/views/admin/anggota/edit.php

Tampilan Edit Anggota
Gambar: Pratinjau antarmuka pembaruan data dan reset password akun siswa (/admin/anggota/edit/{id}).
application/views/admin/anggota/edit.php PHP
<?php $this->load->view('layouts/header'); ?>

<div class="app-wrapper">
    <?php $this->load->view('layouts/sidebar_admin'); ?>

    <div class="app-main">
        <?php $this->load->view('layouts/navbar'); ?>

        <main class="app-content">
            <div class="mb-3">
                <a href="<?= site_url('admin/anggota') ?>" class="text-decoration-none text-secondary small fw-semibold">
                    <i class="bi bi-chevron-left"></i> Kembali ke Data Anggota
                </a>
            </div>

            <div class="page-header">
                <div>
                    <h1 class="page-title">Edit Anggota</h1>
                    <p class="page-subtitle">Perbarui data siswa <?= html_escape($anggota->nama) ?></p>
                </div>
            </div>

            <?php $this->load->view('layouts/flash_message'); ?>

            <div class="card border-0 form-page">
                <div class="card-body p-4">
                    <?= form_open_multipart('admin/anggota/edit/' . $anggota->id) ?>
                        <div class="mb-3">
                            <label for="nama" class="form-label">Nama Lengkap Siswa <span class="text-danger">*</span></label>
                            <input type="text" class="form-control" id="nama" name="nama" value="<?= set_value('nama', $anggota->nama) ?>" required>
                        </div>

                        <div class="row g-3 mb-3">
                            <div class="col-md-6">
                                <label for="nis" class="form-label">NIS (Nomor Induk Siswa) <span class="text-danger">*</span></label>
                                <input type="text" class="form-control" id="nis" name="nis" value="<?= set_value('nis', $anggota->nis) ?>" required>
                            </div>
                            <div class="col-md-6">
                                <label for="kelas" class="form-label">Kelas <span class="text-danger">*</span></label>
                                <input type="text" class="form-control" id="kelas" name="kelas" value="<?= set_value('kelas', $anggota->kelas) ?>" required>
                            </div>
                        </div>

                        <div class="row g-3 mb-3">
                            <div class="col-md-6">
                                <label for="username" class="form-label">Username Login <span class="text-danger">*</span></label>
                                <input type="text" class="form-control" id="username" name="username" value="<?= set_value('username', $anggota->username) ?>" required>
                            </div>
                            <div class="col-md-6">
                                <label for="password" class="form-label">Ganti Password (Kosongkan jika tidak diubah)</label>
                                <input type="password" class="form-control" id="password" name="password" placeholder="Isi hanya jika ingin reset">
                            </div>
                        </div>

                        <div class="mb-3">
                            <label for="foto" class="form-label">Ganti Foto Profil (Opsional)</label>
                            <input type="file" class="form-control" id="foto" name="foto" accept="image/png, image/jpeg, image/webp">
                            <div class="form-text">Biarkan kosong jika tidak ingin mengubah foto. Format: JPG, PNG, WEBP.</div>
                            <?php if (!empty($anggota->foto) && file_exists(FCPATH . 'uploads/profile/' . $anggota->foto)): ?>
                                <div class="mt-2 d-flex align-items-center gap-2">
                                    <img src="<?= base_url('uploads/profile/' . $anggota->foto) ?>" alt="Foto Saat Ini" class="rounded-circle border" style="width: 44px; height: 44px; object-fit: cover;">
                                    <span class="small text-muted">Foto profil saat ini</span>
                                </div>
                            <?php endif; ?>
                        </div>

                        <div class="mb-4">
                            <label class="form-label d-block">Status Akun <span class="text-danger">*</span></label>
                            <div class="form-check form-check-inline">
                                <input class="form-check-input" type="radio" name="status" id="statusAktif" value="aktif" <?= set_radio('status', 'aktif', ($anggota->status === 'aktif')) ?>>
                                <label class="form-check-label" for="statusAktif">Aktif</label>
                            </div>
                            <div class="form-check form-check-inline">
                                <input class="form-check-input" type="radio" name="status" id="statusNonaktif" value="nonaktif" <?= set_radio('status', 'nonaktif', ($anggota->status === 'nonaktif')) ?>>
                                <label class="form-check-label" for="statusNonaktif">Nonaktif</label>
                            </div>
                        </div>

                        <div class="d-flex gap-2">
                            <button type="submit" class="btn btn-primary px-4">
                                <i class="bi bi-check-lg"></i> Simpan Perubahan
                            </button>
                            <a href="<?= site_url('admin/anggota') ?>" class="btn btn-light px-4">Batal</a>
                        </div>
                    </form>
                </div>
            </div>
        </main>
    </div>
</div>

<?php $this->load->view('layouts/footer'); ?>
8

View Kartu Detail Anggota (admin/anggota/detail.php)

Lokasi Berkas: application/views/admin/anggota/detail.php

Tampilan Detail Anggota
Gambar: Pratinjau kartu profil anggota dan ringkasan aktivitas peminjaman siswa (/admin/anggota/detail/{id}).
application/views/admin/anggota/detail.php PHP
<?php $this->load->view('layouts/header'); ?>

<div class="app-wrapper">
    <?php $this->load->view('layouts/sidebar_admin'); ?>

    <div class="app-main">
        <?php $this->load->view('layouts/navbar'); ?>

        <main class="app-content">
            <div class="mb-3">
                <a href="<?= site_url('admin/anggota') ?>" class="text-decoration-none text-secondary small fw-semibold">
                    <i class="bi bi-chevron-left"></i> Kembali ke Data Anggota
                </a>
            </div>

            <div class="page-header">
                <div>
                    <h1 class="page-title">Detail Anggota</h1>
                    <p class="page-subtitle">Informasi profil dan riwayat aktivitas peminjaman siswa</p>
                </div>
                <div>
                    <a href="<?= site_url('admin/anggota/edit/' . $anggota->id) ?>" class="btn btn-primary">
                        <i class="bi bi-pencil"></i> Edit Anggota
                    </a>
                </div>
            </div>

            <div class="card border-0 detail-page mb-4">
                <div class="card-body p-4">
                    <div class="d-flex align-items-center gap-3 mb-4">
                        <?= get_user_avatar_html($anggota->foto, $anggota->nama, 64, 'shadow-sm border') ?>
                        <div>
                            <div class="d-flex align-items-center gap-2">
                                <h4 class="fw-bold text-dark mb-0"><?= html_escape($anggota->nama) ?></h4>
                                <?php if ($anggota->status === 'aktif'): ?>
                                    <span class="badge badge-soft-success"><i class="bi bi-check-circle-fill me-1"></i> Aktif</span>
                                <?php else: ?>
                                    <span class="badge badge-soft-danger"><i class="bi bi-dash-circle-fill me-1"></i> Nonaktif</span>
                                <?php endif; ?>
                            </div>
                            <div class="text-secondary"><?= html_escape($anggota->kelas ? $anggota->kelas : 'Kelas tidak diset') ?></div>
                        </div>
                    </div>

                    <div class="row g-3 pt-3 border-top">
                        <div class="col-sm-6 col-md-4">
                            <div class="text-muted small">Nomor Induk Siswa (NIS)</div>
                            <div class="fw-semibold text-dark"><?= html_escape($anggota->nis ? $anggota->nis : 'โ€”') ?></div>
                        </div>
                        <div class="col-sm-6 col-md-4">
                            <div class="text-muted small">Username Login</div>
                            <div class="fw-semibold text-dark"><?= html_escape($anggota->username) ?></div>
                        </div>
                        <div class="col-sm-6 col-md-4">
                            <div class="text-muted small">Tanggal Bergabung</div>
                            <div class="fw-semibold text-dark"><?= format_tanggal_indo(substr($anggota->created_at, 0, 10)) ?></div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Aktivitas Peminjaman Siswa -->
            <div class="card border-0 detail-page">
                <div class="card-header">
                    <i class="bi bi-clock-history me-1 text-primary"></i> Aktivitas Peminjaman Terakhir
                </div>
                <div class="table-responsive">
                    <table class="table table-hover align-middle mb-0">
                        <thead>
                            <tr>
                                <th>KODE TRX</th>
                                <th>JUDUL BUKU</th>
                                <th>TGL PINJAM</th>
                                <th>BATAS KEMBALI</th>
                                <th>TGL KEMBALI</th>
                                <th>STATUS</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php if (empty($transaksi)): ?>
                                <tr>
                                    <td colspan="6" class="text-center py-4 text-muted">
                                        Siswa ini belum pernah meminjam buku.
                                    </td>
                                </tr>
                            <?php else: ?>
                                <?php foreach ($transaksi as $trx): ?>
                                    <tr>
                                        <td class="fw-semibold text-dark"><?= html_escape($trx->kode_transaksi) ?></td>
                                        <td><?= html_escape($trx->judul_buku) ?></td>
                                        <td><?= format_tanggal_indo($trx->tanggal_pinjam) ?></td>
                                        <td><?= format_tanggal_indo($trx->batas_kembali) ?></td>
                                        <td><?= format_tanggal_indo($trx->tanggal_kembali) ?></td>
                                        <td>
                                            <?php if ($trx->status === 'dipinjam'): ?>
                                                <span class="badge badge-soft-warning"><i class="bi bi-clock-history me-1"></i> Dipinjam</span>
                                            <?php else: ?>
                                                <span class="badge badge-soft-success"><i class="bi bi-check-circle me-1"></i> Dikembalikan</span>
                                            <?php endif; ?>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                        </tbody>
                    </table>
                </div>
            </div>
        </main>
    </div>
</div>

<?php $this->load->view('layouts/footer'); ?>
9

View Detail Transaksi & Sirkulasi (admin/transaksi/detail.php)

Lokasi Berkas: application/views/admin/transaksi/detail.php

application/views/admin/transaksi/detail.php PHP
<?php $this->load->view('layouts/header'); ?>

<div class="app-wrapper">
    <?php $this->load->view('layouts/sidebar_admin'); ?>

    <div class="app-main">
        <?php $this->load->view('layouts/navbar'); ?>

        <main class="app-content">
            <div class="mb-3">
                <a href="<?= site_url('admin/transaksi') ?>" class="text-decoration-none text-secondary small fw-semibold">
                    <i class="bi bi-chevron-left"></i> Kembali ke Semua Transaksi
                </a>
            </div>

            <div class="page-header">
                <div>
                    <h1 class="page-title">Detail Transaksi</h1>
                    <p class="page-subtitle">Informasi lengkap transaksi peminjaman perpustakaan</p>
                </div>
            </div>

            <?php $this->load->view('layouts/flash_message'); ?>

            <div class="card border-0 detail-page">
                <div class="card-body p-4">
                    <div class="d-flex justify-content-between align-items-center mb-4 pb-3 border-bottom flex-wrap gap-2">
                        <div>
                            <span class="text-muted small">KODE TRANSAKSI</span>
                            <h4 class="fw-bold text-dark mb-0"><?= html_escape($transaksi->kode_transaksi) ?></h4>
                        </div>
                        <div>
                            <?php if ($transaksi->status === 'dipinjam'): ?>
                                <span class="badge badge-soft-warning fs-6 px-3 py-2"><i class="bi bi-clock-history me-1"></i> Status: Dipinjam</span>
                            <?php else: ?>
                                <span class="badge badge-soft-success fs-6 px-3 py-2"><i class="bi bi-check-circle me-1"></i> Status: Dikembalikan</span>
                            <?php endif; ?>
                        </div>
                    </div>

                    <!-- Peminjam Info -->
                    <h6 class="fw-bold text-dark text-uppercase small mb-3" style="letter-spacing: .05em;">
                        <i class="bi bi-person text-primary me-1"></i> Informasi Peminjam
                    </h6>
                    <div class="row g-3 mb-4 p-3 bg-light rounded">
                        <div class="col-sm-6 col-md-4">
                            <div class="text-muted small">Nama Siswa</div>
                            <div class="fw-semibold text-dark"><?= html_escape($transaksi->nama_siswa) ?></div>
                        </div>
                        <div class="col-sm-6 col-md-4">
                            <div class="text-muted small">NIS</div>
                            <div class="fw-semibold text-dark"><?= html_escape($transaksi->nis ? $transaksi->nis : 'โ€”') ?></div>
                        </div>
                        <div class="col-sm-6 col-md-4">
                            <div class="text-muted small">Kelas</div>
                            <div class="fw-semibold text-dark"><?= html_escape($transaksi->kelas ? $transaksi->kelas : 'โ€”') ?></div>
                        </div>
                    </div>

                    <!-- Buku Info -->
                    <h6 class="fw-bold text-dark text-uppercase small mb-3" style="letter-spacing: .05em;">
                        <i class="bi bi-book text-primary me-1"></i> Informasi Buku
                    </h6>
                    <div class="row g-3 mb-4 p-3 bg-light rounded">
                        <div class="col-sm-6 col-md-3">
                            <div class="text-muted small">Kode Buku</div>
                            <div class="fw-semibold text-dark"><?= html_escape($transaksi->kode_buku) ?></div>
                        </div>
                        <div class="col-sm-6 col-md-6">
                            <div class="text-muted small">Judul Buku</div>
                            <div class="fw-semibold text-dark"><?= html_escape($transaksi->judul_buku) ?></div>
                        </div>
                        <div class="col-sm-6 col-md-3">
                            <div class="text-muted small">Lokasi Rak</div>
                            <div class="fw-semibold text-dark"><?= html_escape($transaksi->rak ? $transaksi->rak : 'โ€”') ?></div>
                        </div>
                    </div>

                    <!-- Waktu Peminjaman Info -->
                    <h6 class="fw-bold text-dark text-uppercase small mb-3" style="letter-spacing: .05em;">
                        <i class="bi bi-calendar-event text-primary me-1"></i> Tanggal & Periode Pinjam
                    </h6>
                    <div class="row g-3 mb-4 p-3 bg-light rounded">
                        <div class="col-sm-6 col-md-4">
                            <div class="text-muted small">Tanggal Pinjam</div>
                            <div class="fw-semibold text-dark"><?= format_tanggal_indo($transaksi->tanggal_pinjam) ?></div>
                        </div>
                        <div class="col-sm-6 col-md-4">
                            <div class="text-muted small">Batas Waktu Pengembalian</div>
                            <div class="fw-semibold <?= (strtotime($transaksi->batas_kembali) < strtotime(date('Y-m-d')) && $transaksi->status === 'dipinjam') ? 'text-danger' : 'text-dark' ?>">
                                <?= format_tanggal_indo($transaksi->batas_kembali) ?>
                            </div>
                        </div>
                        <div class="col-sm-6 col-md-4">
                            <div class="text-muted small">Tanggal Dikembalikan</div>
                            <div class="fw-semibold text-dark"><?= format_tanggal_indo($transaksi->tanggal_kembali) ?></div>
                        </div>
                    </div>

                    <!-- Tombol Aksi Pengembalian Admin -->
                    <?php if ($transaksi->status === 'dipinjam'): ?>
                        <div class="pt-2 border-top d-flex justify-content-end">
                            <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#prosesKembaliModal">
                                <i class="bi bi-check2-circle"></i> Proses Pengembalian Buku
                            </button>
                        </div>

                        <!-- Modal Konfirmasi Pengembalian -->
                        <div class="modal fade" id="prosesKembaliModal" tabindex="-1" aria-hidden="true">
                            <div class="modal-dialog modal-dialog-centered" style="max-width: 420px;">
                                <div class="modal-content border-0 shadow">
                                    <div class="modal-header border-bottom-0 pb-0">
                                        <h6 class="modal-title fw-bold text-dark">Proses Pengembalian?</h6>
                                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                    </div>
                                    <div class="modal-body py-3">
                                        <p class="mb-2 text-secondary small">
                                            Konfirmasi bahwa buku <strong>"<?= html_escape($transaksi->judul_buku) ?>"</strong> telah diterima dari siswa <strong><?= html_escape($transaksi->nama_siswa) ?></strong>.
                                        </p>
                                        <p class="mb-0 text-muted small">
                                            Stok buku akan bertambah kembali secara otomatis.
                                        </p>
                                    </div>
                                    <div class="modal-footer border-top-0 pt-0">
                                        <button type="button" class="btn btn-light btn-sm" data-bs-dismiss="modal">Batal</button>
                                        <a href="<?= site_url('admin/transaksi/proses_kembali/' . $transaksi->id) ?>" class="btn btn-primary btn-sm">
                                            Ya, Proses Pengembalian
                                        </a>
                                    </div>
                                </div>
                            </div>
                        </div>
                    <?php endif; ?>
                </div>
            </div>
        </main>
    </div>
</div>

<?php $this->load->view('layouts/footer'); ?>