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
<?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
<?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
<?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
Gambar: Pratinjau tabel manajemen akun anggota siswa dengan filter status dan menu aksi (/admin/anggota).
<?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
Gambar: Pratinjau antarmuka sirkulasi peminjaman dengan filter status transaksi (/admin/transaksi).
<?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
Gambar: Pratinjau antarmuka pendaftaran akun anggota siswa baru oleh Admin (/admin/anggota/tambah).
<?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
Gambar: Pratinjau antarmuka pembaruan data dan reset password akun siswa (/admin/anggota/edit/{id}).
<?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
Gambar: Pratinjau kartu profil anggota dan ringkasan aktivitas peminjaman siswa (/admin/anggota/detail/{id}).
<?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
<?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'); ?>