Modul 07: Portal Siswa (Katalog, Pinjam & Buku Saya)

Modul 07: Portal Siswa (Katalog, Pinjam & Buku Saya)

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

Controller Dashboard Siswa (user/Dashboard.php)

Lokasi Berkas: application/controllers/user/Dashboard.php

Dashboard Siswa
Gambar: Pratinjau antarmuka dashboard siswa dengan counter buku pinjaman aktif.
application/controllers/user/Dashboard.php PHP
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * User Dashboard Controller
 */
class Dashboard extends CI_Controller {

    public function __construct() {
        parent::__construct();
        check_user();
        $this->load->model('Dashboard_model');
        $this->load->model('Transaksi_model');
    }

    public function index() {
        $user_id = $this->session->userdata('user_id');

        $data['title'] = 'Dashboard Siswa - SIPAS';
        $data['stats'] = $this->Dashboard_model->get_user_stats($user_id);
        $data['buku_pinjam'] = $this->Transaksi_model->get_active_by_user($user_id);

        $this->load->view('user/dashboard', $data);
    }
}
๐Ÿ’ก Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
2

Controller Katalog Buku Siswa (user/Buku.php)

Lokasi Berkas: application/controllers/user/Buku.php

Katalog Buku Siswa
Gambar: Pratinjau katalog pencarian buku siswa format kartu 3 kolom.
application/controllers/user/Buku.php PHP
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * User Buku Controller
 * Siswa mencari buku, melihat detail buku, dan meminjam buku
 */
class Buku extends CI_Controller {

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

    /**
     * Katalog / Pencarian Buku Siswa (Grid View Card)
     */
    public function index() {
        $search = $this->input->get('keyword', TRUE);
        $kategori = $this->input->get('kategori', TRUE);

        $limit = 9; // 3x3 Grid
        $page = $this->input->get('page') ? (int) $this->input->get('page') : 1;
        $offset = ($page - 1) * $limit;

        $total_rows = $this->Buku_model->count_all($search, $kategori);

        // Pagination setup
        $config['base_url'] = site_url('user/buku');
        $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 justify-content-center 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'] = 'Koleksi Buku - SIPAS - Sistem Informasi Perpustakaan Ardli Sela';
        $data['buku'] = $this->Buku_model->search($search, $kategori, $limit, $offset);
        $data['categories'] = $this->Buku_model->get_categories();
        $data['pagination'] = $this->pagination->create_links();
        $data['total_rows'] = $total_rows;
        $data['keyword'] = $search;
        $data['selected_kategori'] = $kategori;

        $this->load->view('user/buku/index', $data);
    }

    /**
     * Detail Buku Siswa
     */
    public function detail($id) {
        $buku = $this->Buku_model->get_by_id($id);
        if (!$buku) {
            $this->session->set_flashdata('error', 'Buku tidak ditemukan.');
            redirect('user/buku');
        }

        $user_id = $this->session->userdata('user_id');
        $is_borrowed = $this->Transaksi_model->is_book_borrowed_by_user($user_id, $id);

        $data['title'] = 'Detail Buku: ' . $buku->judul;
        $data['buku'] = $buku;
        $data['is_borrowed'] = $is_borrowed;

        $this->load->view('user/buku/detail', $data);
    }

    /**
     * Proses Peminjaman Buku
     */
    public function pinjam($id) {
        $user_id = $this->session->userdata('user_id');

        // Panggil model transaksi yang memuat database transaction dan 4 level validasi
        $result = $this->Transaksi_model->create_peminjaman($user_id, $id);

        if ($result['status']) {
            $this->session->set_flashdata('success', $result['message']);
            redirect('user/peminjaman');
        } else {
            $this->session->set_flashdata('error', $result['message']);
            redirect('user/buku/detail/' . $id);
        }
    }
}
๐Ÿ’ก Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
3

Controller Peminjaman & Buku Saya (user/Peminjaman.php)

Lokasi Berkas: application/controllers/user/Peminjaman.php

Modal Pinjam Buku
Gambar: Pratinjau modal konfirmasi peminjaman buku mandiri oleh siswa.
application/controllers/user/Peminjaman.php PHP
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * User Peminjaman Controller
 * Buku Saya & Riwayat Peminjaman Siswa
 */
class Peminjaman extends CI_Controller {

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

    /**
     * Halaman Buku Saya (/user/peminjaman)
     * Menampilkan buku yang sedang dipinjam oleh user aktif
     */
    public function index() {
        $user_id = $this->session->userdata('user_id');

        $data['title'] = 'Buku Saya - SIPAS - Sistem Informasi Perpustakaan Ardli Sela';
        $data['buku_pinjam'] = $this->Transaksi_model->get_active_by_user($user_id);

        $this->load->view('user/peminjaman/index', $data);
    }

    /**
     * Siswa melakukan pengembalian buku mandiri
     */
    public function kembalikan($transaksi_id) {
        $user_id = $this->session->userdata('user_id');

        // Panggil model proses pengembalian dengan proteksi user_id
        $result = $this->Transaksi_model->proses_pengembalian($transaksi_id, $user_id);

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

        redirect('user/peminjaman');
    }

    /**
     * Halaman Riwayat Transaksi Peminjaman (/user/riwayat)
     */
    public function riwayat() {
        $user_id = $this->session->userdata('user_id');
        $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_by_user($user_id, $status, $search);

        // Pagination setup
        $config['base_url'] = site_url('user/riwayat');
        $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'] = 'Riwayat Peminjaman - SIPAS';
        $data['riwayat'] = $this->Transaksi_model->get_by_user($user_id, $status, $search, $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 ? $status : 'semua';

        $this->load->view('user/peminjaman/riwayat', $data);
    }
}
๐Ÿ’ก Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
4

Controller Profil Anggota Siswa (user/Profil.php)

Lokasi Berkas: application/controllers/user/Profil.php

Buku Saya & Pengembalian
Gambar: Pratinjau halaman buku saya dan tombol pengembalian buku mandiri.
application/controllers/user/Profil.php PHP
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * User Profil Controller
 */
class Profil extends CI_Controller {

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

    public function index() {
        $user_id = $this->session->userdata('user_id');
        $user = $this->User_model->get_by_id($user_id);

        $data['title'] = 'Profil Saya - SIPAS - Sistem Informasi Perpustakaan Ardli Sela';
        $data['user'] = $user;
        $data['total_pinjam'] = $this->Transaksi_model->count_by_user($user_id);

        $this->load->view('user/profil/index', $data);
    }

    /**
     * Update Foto Profil Siswa
     */
    public function update_foto() {
        $user_id = $this->session->userdata('user_id');
        $user = $this->User_model->get_by_id($user_id);

        if (empty($_FILES['foto']['name'])) {
            $this->session->set_flashdata('error', 'Silakan pilih file foto terlebih dahulu.');
            redirect('user/profil');
            return;
        }

        $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);
        if ($this->upload->do_upload('foto')) {
            $upload_res = $this->upload->data();
            $new_foto = $upload_res['file_name'];

            // Update di DB
            $this->User_model->update($user_id, [
                'foto' => $new_foto,
                'updated_at' => date('Y-m-d H:i:s')
            ]);

            // Update session jika perlu
            $this->session->set_userdata('foto', $new_foto);

            // Hapus foto lama jika ada
            if (!empty($user->foto) && file_exists(FCPATH . 'uploads/profile/' . $user->foto)) {
                @unlink(FCPATH . 'uploads/profile/' . $user->foto);
            }

            $this->session->set_flashdata('success', 'Foto profil berhasil diperbarui.');
        } else {
            $this->session->set_flashdata('error', 'Gagal upload foto: ' . $this->upload->display_errors('', ''));
        }

        redirect('user/profil');
    }
}

๐Ÿ’ก Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
5

View Dashboard Portal Siswa (user/dashboard.php)

Lokasi Berkas: application/views/user/dashboard.php

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

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

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

        <main class="app-content">
            <div class="page-header">
                <div>
                    <h1 class="page-title">Selamat Datang, <?= html_escape($this->session->userdata('nama')) ?></h1>
                    <p class="page-subtitle">Temukan dan pinjam berbagai buku pelajaran dan referensi sekolah</p>
                </div>
            </div>

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

            <!-- Stats Row -->
            <div class="row g-3 mb-4">
                <div class="col-sm-4">
                    <div class="card border-0 mb-0">
                        <div class="stat-card">
                            <div class="stat-icon">
                                <i class="bi bi-book"></i>
                            </div>
                            <div>
                                <div class="stat-number"><?= $stats['buku_tersedia'] ?></div>
                                <div class="stat-label">Buku Tersedia</div>
                                <div class="stat-sub">Siap dipinjam</div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="col-sm-4">
                    <div class="card border-0 mb-0">
                        <div class="stat-card">
                            <div class="stat-icon" style="background-color: var(--warning-soft); color: var(--warning);">
                                <i class="bi bi-journal-arrow-up"></i>
                            </div>
                            <div>
                                <div class="stat-number"><?= $stats['sedang_dipinjam'] ?></div>
                                <div class="stat-label">Sedang Dipinjam</div>
                                <div class="stat-sub">Buku aktif kamu</div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="col-sm-4">
                    <div class="card border-0 mb-0">
                        <div class="stat-card">
                            <div class="stat-icon" style="background-color: var(--success-soft); color: var(--success);">
                                <i class="bi bi-check2-circle"></i>
                            </div>
                            <div>
                                <div class="stat-number"><?= $stats['sudah_kembali'] ?></div>
                                <div class="stat-label">Sudah Dikembalikan</div>
                                <div class="stat-sub">Riwayat selesai</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Quick Search Form -->
            <div class="card border-0 mb-4">
                <div class="card-body p-4">
                    <h6 class="fw-bold text-dark mb-2">Cari Buku di Perpustakaan</h6>
                    <form action="<?= site_url('user/buku') ?>" method="GET" class="d-flex gap-2">
                        <div class="search-input-group flex-grow-1">
                            <i class="bi bi-search"></i>
                            <input type="text" class="form-control" name="keyword" placeholder="Ketik judul, pengarang atau kategori buku...">
                        </div>
                        <button type="submit" class="btn btn-primary px-4">
                            Cari Buku
                        </button>
                    </form>
                </div>
            </div>

            <!-- Sedang Kamu Pinjam Section -->
            <div class="card border-0">
                <div class="card-header d-flex justify-content-between align-items-center">
                    <span><i class="bi bi-journal-check me-1 text-primary"></i> Sedang Kamu Pinjam</span>
                    <a href="<?= site_url('user/peminjaman') ?>" class="text-decoration-none small fw-semibold text-primary">
                        Lihat Buku Saya <i class="bi bi-arrow-right"></i>
                    </a>
                </div>
                <div class="card-body p-0">
                    <?php if (empty($buku_pinjam)): ?>
                        <div class="text-center py-5 text-muted">
                            <i class="bi bi-emoji-smile fs-3 d-block mb-2 text-primary"></i>
                            <p class="mb-1 fw-semibold text-dark">Kamu sedang tidak meminjam buku.</p>
                            <span class="small text-secondary">Ayo jelajahi koleksi buku dan pinjam sekarang!</span>
                        </div>
                    <?php else: ?>
                        <div class="list-group list-group-flush">
                            <?php foreach ($buku_pinjam as $item): ?>
                                <div class="list-group-item d-flex justify-content-between align-items-center p-3 flex-wrap gap-2">
                                    <div class="d-flex align-items-center gap-3">
                                        <div class="book-card-icon mb-0" style="width: 40px; height: 40px; font-size: 1.25rem;">
                                            <i class="bi bi-book"></i>
                                        </div>
                                        <div>
                                            <div class="fw-bold text-dark mb-0"><?= html_escape($item->judul_buku) ?></div>
                                            <div class="text-muted small">
                                                Batas pengembalian: 
                                                <span class="<?= (strtotime($item->batas_kembali) < strtotime(date('Y-m-d'))) ? 'text-danger fw-bold' : '' ?>">
                                                    <?= format_tanggal_indo($item->batas_kembali) ?>
                                                </span>
                                            </div>
                                        </div>
                                    </div>
                                    <div class="d-flex align-items-center gap-2">
                                        <span class="badge badge-soft-warning">Dipinjam</span>
                                        <a href="<?= site_url('user/buku/detail/' . $item->buku_id) ?>" class="btn btn-light btn-sm">
                                            Lihat Detail
                                        </a>
                                    </div>
                                </div>
                            <?php endforeach; ?>
                        </div>
                    <?php endif; ?>
                </div>
            </div>
        </main>
    </div>
</div>

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

View Katalog Buku Siswa (user/buku/index.php)

Lokasi Berkas: application/views/user/buku/index.php

application/views/user/buku/index.php PHP
<?php $this->load->view('layouts/header'); ?>

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

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

        <main class="app-content">
            <div class="page-header">
                <div>
                    <h1 class="page-title">Cari Buku</h1>
                    <p class="page-subtitle">Temukan koleksi buku yang tersedia di perpustakaan sekolah</p>
                </div>
            </div>

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

            <!-- Search Bar Standard -->
            <div class="card border-0 mb-4">
                <div class="card-body p-3">
                    <form action="<?= site_url('user/buku') ?>" method="GET" class="row g-2 align-items-center">
                        <div class="col-md-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 judul buku, kode, penulis atau kategori...">
                            </div>
                        </div>
                        <div class="col-md-3">
                            <select name="kategori" class="form-select" onchange="this.form.submit()">
                                <option value="">Semua Kategori</option>
                                <?php foreach ($categories as $cat): ?>
                                    <option value="<?= html_escape($cat->kategori) ?>" <?= $selected_kategori === $cat->kategori ? 'selected' : '' ?>>
                                        <?= html_escape($cat->kategori) ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="col-md-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_kategori)): ?>
                                <a href="<?= site_url('user/buku') ?>" class="btn btn-light" title="Reset filter">
                                    <i class="bi bi-x-lg"></i>
                                </a>
                            <?php endif; ?>
                        </div>
                    </form>
                </div>
            </div>

            <!-- Book Grid (3 Card per Row Desktop, 2 Tablet, 1 Mobile) -->
            <?php if (empty($buku)): ?>
                <div class="card border-0">
                    <div class="card-body">
                        <div class="empty-state">
                            <div class="empty-state-icon"><i class="bi bi-search"></i></div>
                            <h6 class="fw-bold text-dark">Tidak ada hasil ditemukan</h6>
                            <p class="text-secondary small mb-3">
                                <?php if (!empty($keyword)): ?>
                                    Tidak ditemukan buku dengan kata kunci "<strong><?= html_escape($keyword) ?></strong>".
                                <?php else: ?>
                                    Belum ada buku dalam katalog perpustakaan.
                                <?php endif; ?>
                            </p>
                            <?php if (!empty($keyword) || !empty($selected_kategori)): ?>
                                <a href="<?= site_url('user/buku') ?>" class="btn btn-light btn-sm">
                                    Hapus Pencarian
                                </a>
                            <?php endif; ?>
                        </div>
                    </div>
                </div>
            <?php else: ?>
                <div class="row row-cols-1 row-cols-md-2 row-cols-xl-3 g-4 mb-4">
                    <?php foreach ($buku as $item): ?>
                        <div class="col">
                            <div class="card book-card border-0">
                                <div class="card-body p-4 d-flex flex-column justify-content-between">
                                    <div>
                                        <div class="d-flex justify-content-between align-items-start mb-3">
                                            <?php $cover_url = get_book_cover_url($item->gambar); if ($cover_url): ?>
                                                <img src="<?= $cover_url ?>" alt="<?= html_escape($item->judul) ?>" class="rounded border shadow-sm" style="width: 52px; height: 70px; object-fit: cover;" loading="lazy">
                                            <?php else: ?>
                                                <div class="book-card-icon">
                                                    <i class="bi bi-book"></i>
                                                </div>
                                            <?php endif; ?>
                                            <span class="badge badge-soft-secondary"><?= html_escape($item->kode_buku) ?></span>
                                        </div>

                                        <h5 class="book-title"><?= html_escape($item->judul) ?></h5>
                                        <div class="book-author"><?= html_escape($item->penulis ? $item->penulis : 'Penulis tidak dicantumkan') ?></div>

                                        <div class="mb-3">
                                            <span class="badge badge-soft-info me-1"><?= html_escape($item->kategori ? $item->kategori : 'Umum') ?></span>
                                            <span class="text-muted small">Rak: <?= html_escape($item->rak ? $item->rak : 'โ€”') ?></span>
                                        </div>
                                    </div>

                                    <div class="pt-3 border-top mt-2 d-flex justify-content-between align-items-center">
                                        <div>
                                            <?php if ($item->stok > 0): ?>
                                                <span class="text-success small fw-semibold">
                                                    <i class="bi bi-check-circle-fill me-1"></i> <?= $item->stok ?> tersedia
                                                </span>
                                            <?php else: ?>
                                                <span class="text-danger small fw-semibold">
                                                    <i class="bi bi-x-circle-fill me-1"></i> Stok habis
                                                </span>
                                            <?php endif; ?>
                                        </div>
                                        <a href="<?= site_url('user/buku/detail/' . $item->id) ?>" class="btn btn-primary btn-sm px-3">
                                            Lihat Detail
                                        </a>
                                    </div>
                                </div>
                            </div>
                        </div>
                    <?php endforeach; ?>
                </div>

                <!-- Pagination -->
                <?php if ($total_rows > 9): ?>
                    <div class="d-flex justify-content-center mb-4">
                        <?= $pagination ?>
                    </div>
                <?php endif; ?>
            <?php endif; ?>
        </main>
    </div>
</div>

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

View Buku Saya & Modal Pengembalian (user/peminjaman/index.php)

Lokasi Berkas: application/views/user/peminjaman/index.php

application/views/user/peminjaman/index.php PHP
<?php $this->load->view('layouts/header'); ?>

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

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

        <main class="app-content">
            <div class="page-header">
                <div>
                    <h1 class="page-title">Buku Saya</h1>
                    <p class="page-subtitle">Daftar buku yang sedang kamu pinjam saat ini</p>
                </div>
            </div>

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

            <?php if (empty($buku_pinjam)): ?>
                <div class="card border-0">
                    <div class="card-body">
                        <div class="empty-state">
                            <div class="empty-state-icon"><i class="bi bi-book"></i></div>
                            <h6 class="fw-bold text-dark">Tidak ada buku yang sedang dipinjam</h6>
                            <p class="text-secondary small mb-3">Kamu belum meminjam buku apa pun saat ini.</p>
                            <a href="<?= site_url('user/buku') ?>" class="btn btn-primary btn-sm">
                                <i class="bi bi-search"></i> Cari Buku Sekarang
                            </a>
                        </div>
                    </div>
                </div>
            <?php else: ?>
                <div class="row g-3">
                    <?php foreach ($buku_pinjam as $item): ?>
                        <div class="col-md-6 col-lg-6">
                            <div class="card border-0 p-3 h-100">
                                <div class="d-flex justify-content-between align-items-start mb-3">
                                    <div class="d-flex gap-3 align-items-center">
                                        <?php $cover_url = get_book_cover_url($item->gambar_buku); if ($cover_url): ?>
                                            <img src="<?= $cover_url ?>" alt="<?= html_escape($item->judul_buku) ?>" class="rounded border shadow-sm" style="width: 46px; height: 60px; object-fit: cover;" loading="lazy">
                                        <?php else: ?>
                                            <div class="book-card-icon mb-0" style="width: 46px; height: 46px; font-size: 1.35rem;">
                                                <i class="bi bi-book"></i>
                                            </div>
                                        <?php endif; ?>
                                        <div>
                                            <span class="badge badge-soft-secondary mb-1"><?= html_escape($item->kode_buku) ?></span>
                                            <h5 class="fw-bold text-dark mb-1" style="font-size: 1.05rem;"><?= html_escape($item->judul_buku) ?></h5>
                                            <div class="text-secondary small"><?= html_escape($item->penulis ? $item->penulis : 'โ€”') ?></div>
                                        </div>
                                    </div>
                                    <span class="badge badge-soft-warning">Dipinjam</span>
                                </div>

                                <div class="bg-light p-3 rounded mb-3 small">
                                    <div class="d-flex justify-content-between mb-1">
                                        <span class="text-muted">Kode Transaksi:</span>
                                        <span class="fw-semibold text-dark"><?= html_escape($item->kode_transaksi) ?></span>
                                    </div>
                                    <div class="d-flex justify-content-between mb-1">
                                        <span class="text-muted">Tanggal Pinjam:</span>
                                        <span class="fw-semibold text-dark"><?= format_tanggal_indo($item->tanggal_pinjam) ?></span>
                                    </div>
                                    <div class="d-flex justify-content-between">
                                        <span class="text-muted">Batas Pengembalian:</span>
                                        <span class="fw-semibold <?= (strtotime($item->batas_kembali) < strtotime(date('Y-m-d'))) ? 'text-danger' : 'text-dark' ?>">
                                            <?= format_tanggal_indo($item->batas_kembali) ?>
                                        </span>
                                    </div>
                                </div>

                                <div class="d-flex justify-content-end gap-2 mt-auto">
                                    <a href="<?= site_url('user/buku/detail/' . $item->buku_id) ?>" class="btn btn-light btn-sm">
                                        Detail Buku
                                    </a>
                                    <button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#kembalikanModal<?= $item->id ?>">
                                        <i class="bi bi-arrow-counterclockwise"></i> Kembalikan Buku
                                    </button>
                                </div>

                                <!-- Modal Konfirmasi Pengembalian (Sesuai UI/UX Spec 27) -->
                                <div class="modal fade" id="kembalikanModal<?= $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">Kembalikan Buku?</h6>
                                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                            </div>
                                            <div class="modal-body py-3">
                                                <p class="text-secondary small mb-2">Pastikan buku fisik sudah siap dikembalikan ke perpustakaan:</p>
                                                <div class="p-2 bg-light rounded mb-2">
                                                    <strong class="text-dark d-block"><?= html_escape($item->judul_buku) ?></strong>
                                                    <span class="text-muted small">Kode: <?= html_escape($item->kode_buku) ?></span>
                                                </div>
                                                <p class="text-muted small mb-0">Setelah dikembalikan, status transaksi akan dipindahkan ke riwayat.</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('user/peminjaman/kembalikan/' . $item->id) ?>" class="btn btn-primary btn-sm">
                                                    Ya, Kembalikan
                                                </a>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </main>
    </div>
</div>

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

View Detail Buku Siswa & Modal Pinjam (user/buku/detail.php)

Lokasi Berkas: application/views/user/buku/detail.php

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

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

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

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

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

            <div class="card border-0 detail-page mb-4">
                <div class="card-body p-4">
                    <div class="d-flex flex-column flex-md-row gap-4 mb-4 pb-4 border-bottom align-items-center align-items-md-start">
                        <?php $cover_url = get_book_cover_url($buku->gambar); if ($cover_url): ?>
                            <img src="<?= $cover_url ?>" alt="<?= html_escape($buku->judul) ?>" class="rounded border shadow-sm mx-auto mx-md-0" style="width: 100px; height: 135px; object-fit: cover; flex-shrink: 0;">
                        <?php else: ?>
                            <div class="book-card-icon mx-auto mx-md-0" style="width: 84px; height: 84px; font-size: 2.5rem; border-radius: 14px;">
                                <i class="bi bi-book"></i>
                            </div>
                        <?php endif; ?>

                        <div class="flex-grow-1 text-center text-md-start">
                            <span class="badge badge-soft-secondary mb-2"><?= html_escape($buku->kode_buku) ?></span>
                            <h3 class="fw-bold text-dark mb-1"><?= html_escape($buku->judul) ?></h3>
                            <div class="text-secondary mb-2"><?= html_escape($buku->penulis ? $buku->penulis : 'Penulis tidak dicantumkan') ?></div>

                            <div class="d-flex flex-wrap gap-2 justify-content-center justify-content-md-start align-items-center mb-3">
                                <span class="badge badge-soft-info"><?= html_escape($buku->kategori ? $buku->kategori : 'Umum') ?></span>
                                <span class="text-muted small">|</span>
                                <?php if ($buku->stok > 0): ?>
                                    <span class="badge badge-soft-success"><i class="bi bi-check-circle me-1"></i> <?= $buku->stok ?> buku tersedia</span>
                                <?php else: ?>
                                    <span class="badge badge-soft-danger"><i class="bi bi-x-circle me-1"></i> Stok habis</span>
                                <?php endif; ?>
                            </div>

                            <div class="row g-2 text-start small mb-3">
                                <div class="col-sm-4">
                                    <span class="text-muted">Penerbit:</span> <strong><?= html_escape($buku->penerbit ? $buku->penerbit : 'โ€”') ?></strong>
                                </div>
                                <div class="col-sm-4">
                                    <span class="text-muted">Tahun:</span> <strong><?= $buku->tahun_terbit ? $buku->tahun_terbit : 'โ€”' ?></strong>
                                </div>
                                <div class="col-sm-4">
                                    <span class="text-muted">Lokasi Rak:</span> <strong><?= html_escape($buku->rak ? $buku->rak : 'โ€”') ?></strong>
                                </div>
                            </div>

                            <!-- Tombol Pinjam / Status Pinjam -->
                            <div>
                                <?php if ($is_borrowed): ?>
                                    <button type="button" class="btn btn-warning disabled" disabled>
                                        <i class="bi bi-info-circle"></i> Sedang Kamu Pinjam
                                    </button>
                                <?php elseif ($buku->stok <= 0): ?>
                                    <button type="button" class="btn btn-light disabled" disabled>
                                        <i class="bi bi-dash-circle"></i> Stok Sedang Habis
                                    </button>
                                <?php else: ?>
                                    <button type="button" class="btn btn-primary px-4" data-bs-toggle="modal" data-bs-target="#confirmPinjamModal">
                                        <i class="bi bi-journal-plus"></i> Pinjam Buku Ini
                                    </button>
                                <?php endif; ?>
                            </div>
                        </div>
                    </div>

                    <!-- Tentang Buku -->
                    <div>
                        <h6 class="fw-bold text-dark mb-2">Tentang Buku</h6>
                        <div class="p-3 bg-light rounded text-secondary" style="line-height: 1.6;">
                            <?= !empty($buku->deskripsi) ? nl2br(html_escape($buku->deskripsi)) : 'Tidak ada ringkasan deskripsi untuk buku ini.' ?>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Modal Konfirmasi Peminjaman (Sesuai UI/UX Spec 25) -->
            <?php if (!$is_borrowed && $buku->stok > 0): ?>
                <div class="modal fade" id="confirmPinjamModal" tabindex="-1" aria-labelledby="confirmPinjamLabel" 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" id="confirmPinjamLabel">Pinjam Buku?</h6>
                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                            </div>
                            <div class="modal-body py-3">
                                <p class="text-secondary small mb-3">Kamu akan meminjam koleksi perpustakaan:</p>
                                <div class="p-3 bg-light rounded mb-3">
                                    <div class="fw-bold text-dark"><?= html_escape($buku->judul) ?></div>
                                    <div class="text-muted small"><?= html_escape($buku->penulis) ?></div>
                                </div>
                                <div class="row g-2 small text-secondary">
                                    <div class="col-6">Tanggal Pinjam:</div>
                                    <div class="col-6 fw-semibold text-dark text-end"><?= format_tanggal_indo(date('Y-m-d')) ?></div>
                                    <div class="col-6">Batas Waktu:</div>
                                    <div class="col-6 fw-semibold text-dark text-end"><?= format_tanggal_indo(date('Y-m-d', strtotime('+7 days'))) ?></div>
                                </div>
                            </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('user/buku/pinjam/' . $buku->id) ?>" class="btn btn-primary btn-sm">
                                    Ya, Pinjam Buku
                                </a>
                            </div>
                        </div>
                    </div>
                </div>
            <?php endif; ?>
        </main>
    </div>
</div>

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

View Riwayat Peminjaman Siswa (user/peminjaman/riwayat.php)

Lokasi Berkas: application/views/user/peminjaman/riwayat.php

application/views/user/peminjaman/riwayat.php PHP
<?php $this->load->view('layouts/header'); ?>

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

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

        <main class="app-content">
            <div class="page-header">
                <div>
                    <h1 class="page-title">Riwayat Peminjaman</h1>
                    <p class="page-subtitle">Lihat seluruh riwayat aktivitas peminjaman buku kamu</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('user/riwayat') ?>" 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 judul buku atau kode transaksi...">
                            </div>
                        </div>
                        <div class="col-md-3 col-lg-3">
                            <select name="status" class="form-select" onchange="this.form.submit()">
                                <option value="semua">Semua Status</option>
                                <option value="dipinjam" <?= $selected_status === 'dipinjam' ? 'selected' : '' ?>>Sedang Dipinjam</option>
                                <option value="dikembalikan" <?= $selected_status === 'dikembalikan' ? 'selected' : '' ?>>Sudah Dikembalikan</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) && $selected_status !== 'semua')): ?>
                                <a href="<?= site_url('user/riwayat') ?>" class="btn btn-light" 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>BUKU</th>
                                <th>TANGGAL PINJAM</th>
                                <th>TANGGAL KEMBALI</th>
                                <th>STATUS</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php if (empty($riwayat)): ?>
                                <tr>
                                    <td colspan="5">
                                        <div class="empty-state">
                                            <div class="empty-state-icon"><i class="bi bi-clock-history"></i></div>
                                            <h6 class="fw-bold text-dark">Belum ada riwayat transaksi</h6>
                                            <p class="text-secondary small mb-0">Riwayat peminjaman buku kamu akan muncul di sini.</p>
                                        </div>
                                    </td>
                                </tr>
                            <?php else: ?>
                                <?php foreach ($riwayat 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->judul_buku) ?></div>
                                            <div class="text-muted small">Kode: <?= html_escape($item->kode_buku) ?> | Penulis: <?= html_escape($item->penulis ? $item->penulis : 'โ€”') ?></div>
                                        </td>
                                        <td><?= format_tanggal_indo($item->tanggal_pinjam) ?></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>
                                    </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 ?> riwayat
                        </span>
                        <div>
                            <?= $pagination ?>
                        </div>
                    </div>
                <?php endif; ?>
            </div>
        </main>
    </div>
</div>

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

View Profil Siswa & Upload Foto (user/profil/index.php)

Lokasi Berkas: application/views/user/profil/index.php

application/views/user/profil/index.php PHP
<?php $this->load->view('layouts/header'); ?>

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

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

        <main class="app-content">
            <div class="page-header">
                <div>
                    <h1 class="page-title">Profil Saya</h1>
                    <p class="page-subtitle">Informasi identitas akun anggota perpustakaan digital</p>
                </div>
            </div>

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

            <div class="card border-0 detail-page" style="max-width: 680px;">
                <div class="card-body p-4">
                    <div class="text-center pb-4 mb-4 border-bottom">
                        <div class="mx-auto mb-3 d-inline-block position-relative">
                            <?= get_user_avatar_html($user->foto, $user->nama, 84, 'shadow-sm border') ?>
                        </div>
                        <h4 class="fw-bold text-dark mb-1"><?= html_escape($user->nama) ?></h4>
                        <div class="text-secondary mb-3"><?= html_escape($user->kelas ? $user->kelas : 'Siswa Sekolah') ?></div>

                        <!-- Form Unggah Foto Profil -->
                        <div class="d-flex justify-content-center">
                            <button type="button" class="btn btn-outline-primary btn-sm px-3" data-bs-toggle="modal" data-bs-target="#modalUploadFoto">
                                <i class="bi bi-camera me-1"></i> Ganti Foto Profil
                            </button>
                        </div>
                    </div>

                    <!-- Modal Ganti Foto Profil -->
                    <div class="modal fade" id="modalUploadFoto" tabindex="-1" aria-labelledby="modalUploadFotoLabel" 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" id="modalUploadFotoLabel">Perbarui Foto Profil</h6>
                                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                </div>
                                <?= form_open_multipart('user/profil/update_foto') ?>
                                    <div class="modal-body py-3">
                                        <p class="text-muted small mb-3">Pilih file gambar foto profil baru (Format: JPG, PNG, WEBP. Maks 2MB).</p>
                                        <input type="file" name="foto" class="form-control" accept="image/png, image/jpeg, image/webp" required>
                                    </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>
                                        <button type="submit" class="btn btn-primary btn-sm px-3">
                                            <i class="bi bi-upload me-1"></i> Simpan Foto
                                        </button>
                                    </div>
                                <?= form_close() ?>
                            </div>
                        </div>
                    </div>

                    <div class="row g-3">
                        <div class="col-sm-6">
                            <div class="p-3 bg-light rounded">
                                <span class="text-muted small d-block">Nomor Induk Siswa (NIS)</span>
                                <strong class="text-dark fs-6"><?= html_escape($user->nis ? $user->nis : 'โ€”') ?></strong>
                            </div>
                        </div>
                        <div class="col-sm-6">
                            <div class="p-3 bg-light rounded">
                                <span class="text-muted small d-block">Kelas</span>
                                <strong class="text-dark fs-6"><?= html_escape($user->kelas ? $user->kelas : 'โ€”') ?></strong>
                            </div>
                        </div>
                        <div class="col-sm-6">
                            <div class="p-3 bg-light rounded">
                                <span class="text-muted small d-block">Username</span>
                                <strong class="text-dark fs-6"><?= html_escape($user->username) ?></strong>
                            </div>
                        </div>
                        <div class="col-sm-6">
                            <div class="p-3 bg-light rounded">
                                <span class="text-muted small d-block">Status Keanggotaan</span>
                                <span class="badge badge-soft-success fs-6"><i class="bi bi-check-circle-fill me-1"></i> Aktif</span>
                            </div>
                        </div>
                        <div class="col-12">
                            <div class="p-3 bg-light rounded d-flex justify-content-between align-items-center">
                                <div>
                                    <span class="text-muted small d-block">Total Aktivitas Peminjaman</span>
                                    <strong class="text-dark fs-6"><?= $total_pinjam ?> Kali Meminjam Buku</strong>
                                </div>
                                <a href="<?= site_url('user/riwayat') ?>" class="btn btn-light btn-sm">
                                    Lihat Riwayat
                                </a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </main>
    </div>
</div>

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