Modul 04: Landing Page, Beranda Publik & Autentikasi

Modul 04: Landing Page Publik, Login & Registrasi

Panduan membangun gerbang awal aplikasi: Landing Page publik (etalase buku & statistik ringkas), rute default, serta modul autentikasi multi-role (Admin & Siswa).

Fokus Materi: Homepage Publik & Keamanan Auth Default Route: home (bukan login) Komponen: Home Controller, Auth, 3 View
1

Controller Landing Page Publik (Home.php)

Lokasi Berkas: application/controllers/Home.php

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

/**
 * Landing Page & Homepage Controller
 * Halaman depan publik untuk aplikasi SIPAS Perpustakaan Digital
 */
class Home extends CI_Controller {

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

    public function index() {
        // Ambil data statistik ringkas untuk counter landing page
        $data['stats'] = $this->Dashboard_model->get_admin_stats();

        // Ambil koleksi buku terbaru/populer untuk showcase
        $data['featured_books'] = $this->Buku_model->get_all(8, 0);

        // Ambil daftar kategori buku untuk filter cepat
        $data['categories'] = $this->Buku_model->get_categories();

        $data['title'] = 'SIPAS - Sistem Informasi Perpustakaan Ardli Sela Digital';
        $this->load->view('home', $data);
    }
}
💡 Catatan: Pastikan di application/config/routes.php telah tersetting: $route['default_controller'] = 'home';
2

View Beranda / Landing Page (home.php)

Lokasi Berkas: application/views/home.php

Tampilan Beranda Landing Page Perpustakaan
Gambar: Pratinjau antarmuka Beranda / Landing Page publik SIPAS Digital (Hero Section, Rekomendasi Buku, Alur Peminjaman, & Informasi Perpustakaan).
application/views/home.php PHP
<?php $this->load->view('layouts/header'); ?>

<style>
  .landing-nav {
    background: #FFFFFF;
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 1030;
  }
  .landing-hero {
    background: radial-gradient(130% 120% at 50% 0%, #FFF7ED 0%, #FFFFFF 65%, #F8FAFC 100%);
    padding: 4.5rem 0 3.5rem;
    border-bottom: 1px solid var(--border);
    position: relative;
    overflow: hidden;
  }
  .landing-hero::before {
    content: '';
    position: absolute;
    top: -50%;
    right: -10%;
    width: 600px;
    height: 600px;
    background: radial-gradient(circle, rgba(249, 115, 22, 0.08) 0%, transparent 70%);
    pointer-events: none;
  }
  .badge-hero-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: #FFEDD5;
    color: #C2410C;
    font-size: 12px;
    font-weight: 700;
    padding: 5px 14px;
    border-radius: 9999px;
    margin-bottom: 1rem;
    border: 1px solid #FDBA74;
  }
  .landing-title {
    font-size: 2.75rem;
    font-weight: 800;
    line-height: 1.2;
    letter-spacing: -0.03em;
    color: #0F172A;
    margin-bottom: 1.25rem;
  }
  .landing-title span {
    color: var(--primary);
  }
  .landing-lead {
    font-size: 1.15rem;
    color: var(--text-secondary);
    line-height: 1.65;
    margin-bottom: 2rem;
  }
  .stat-landing-card {
    background: #FFFFFF;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 1.25rem 1.5rem;
    box-shadow: var(--shadow-card);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
  }
  .stat-landing-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 25px rgba(15, 23, 42, 0.08);
  }
  .book-card-hover {
    transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s ease;
    border-radius: 12px;
    overflow: hidden;
  }
  .book-card-hover:hover {
    transform: translateY(-4px);
    box-shadow: 0 12px 24px rgba(15, 23, 42, 0.09) !important;
  }
  .feature-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 14px;
    background: #FFFFFF;
    border: 1px solid var(--border);
    border-radius: 30px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
  }
  .info-step-card {
    background: #FFFFFF;
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 1.5rem;
    height: 100%;
    transition: all 0.2s ease;
  }
  .info-step-card:hover {
    border-color: #FDBA74;
    box-shadow: 0 8px 20px rgba(249, 115, 22, 0.08);
  }
  .footer-landing {
    background: #0F172A;
    color: #94A3B8;
    padding: 3.5rem 0 2rem;
    border-top: 1px solid #1E293B;
  }
</style>

<!-- Public Navigation Bar -->
<nav class="navbar navbar-expand-lg landing-nav py-3">
  <div class="container">
    <a class="navbar-brand d-flex align-items-center gap-2 fw-bold text-dark" href="<?= site_url() ?>">
      <div class="sidebar-brand-icon" style="width: 38px; height: 38px; font-size: 1.25rem; background: var(--primary); color: #FFF; border-radius: 10px; display: flex; align-items: center; justify-content: center;">
        <i class="bi bi-book-half"></i>
      </div>
      <div>
        <span class="fs-5 tracking-tight text-dark" style="font-weight: 800;">SIPAS</span>
        <span class="badge ms-1" style="background: #FFEDD5; color: #C2410C; font-size: 10px; font-weight: 700;">PERPUSTAKAAN</span>
      </div>
    </a>

    <button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navbarLanding" aria-controls="navbarLanding" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarLanding">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0 ms-lg-4">
        <li class="nav-item">
          <a class="nav-link fw-semibold text-dark active" href="#beranda">Beranda</a>
        </li>
        <li class="nav-item">
          <a class="nav-link fw-semibold text-secondary" href="#katalog">Katalog Buku</a>
        </li>
        <li class="nav-item">
          <a class="nav-link fw-semibold text-secondary" href="#layanan">Alur Peminjaman</a>
        </li>
        <li class="nav-item">
          <a class="nav-link fw-semibold text-secondary" href="#tentang">Tentang Perpustakaan</a>
        </li>
      </ul>

      <div class="d-flex align-items-center gap-2">
        <?php if ($this->session->userdata('user_id')): ?>
          <?php if ($this->session->userdata('role') === 'admin'): ?>
            <a href="<?= site_url('admin/dashboard') ?>" class="btn btn-primary px-3 fw-semibold text-white">
              <i class="bi bi-speedometer2 me-1"></i> Panel Admin
            </a>
          <?php else: ?>
            <a href="<?= site_url('user/dashboard') ?>" class="btn btn-primary px-3 fw-semibold text-white">
              <i class="bi bi-grid me-1"></i> Area Siswa
            </a>
          <?php endif; ?>
          <a href="<?= site_url('logout') ?>" class="btn btn-outline-danger px-3 fw-semibold" title="Keluar">
            <i class="bi bi-box-arrow-right"></i>
          </a>
        <?php else: ?>
          <a href="<?= site_url('login') ?>" class="btn btn-light border px-3 fw-semibold text-dark">
            <i class="bi bi-box-arrow-in-right me-1"></i> Masuk
          </a>
          <a href="<?= site_url('register') ?>" class="btn btn-primary px-3 fw-semibold text-white">
            <i class="bi bi-person-plus me-1"></i> Daftar Anggota
          </a>
        <?php endif; ?>
      </div>
    </div>
  </div>
</nav>

<!-- Hero Section -->
<section id="beranda" class="landing-hero">
  <div class="container">
    <div class="row align-items-center g-5">
      <div class="col-lg-7">
        <div class="badge-hero-pill">
          <i class="bi bi-book me-1"></i> RUANG LITERASI &amp; PERPUSTAKAAN DIGITAL
        </div>
        <h1 class="landing-title">
          Temukan Buku Favorit &amp; Perluas Wawasan di <span>Perpustakaan</span> Kami.
        </h1>
        <p class="landing-lead">
          Selamat datang di portal perpustakaan SMK Ardli Sela. Temukan koleksi buku pelajaran, kejuruan, referensi ilmiah, karya sastra, hingga ensiklopedia terkini untuk mendukung kegiatan belajar mengajar.
        </p>

        <div class="d-flex flex-wrap gap-3 mb-4">
          <a href="#katalog" class="btn btn-primary btn-lg px-4 py-3 fw-bold text-white shadow-sm" style="border-radius: 10px;">
            <i class="bi bi-search me-2"></i> Jelajahi Koleksi Buku
          </a>
          <a href="<?= site_url('login') ?>" class="btn btn-outline-secondary btn-lg px-4 py-3 fw-semibold bg-white" style="border-radius: 10px;">
            <i class="bi bi-card-checklist me-2"></i> Masuk ke Akun Peminjam
          </a>
        </div>

        <div class="d-flex flex-wrap gap-2 pt-2">
          <div class="feature-pill"><i class="bi bi-check-circle-fill text-success"></i> Akses Katalog Kapan Saja</div>
          <div class="feature-pill"><i class="bi bi-check-circle-fill text-success"></i> Pencarian Buku Cepat</div>
          <div class="feature-pill"><i class="bi bi-check-circle-fill text-success"></i> Informasi Stok Akurat</div>
        </div>
      </div>

      <div class="col-lg-5">
        <!-- Card Counter Showcase -->
        <div class="row g-3">
          <div class="col-6">
            <div class="stat-landing-card text-center">
              <div class="d-inline-flex p-3 rounded-circle mb-2" style="background: #FFF7ED; color: var(--primary);">
                <i class="bi bi-book fs-3"></i>
              </div>
              <h3 class="fw-bold mb-0 text-dark"><?= number_format(isset($stats['total_buku']) ? $stats['total_buku'] : 0) ?></h3>
              <p class="text-secondary small mb-0 fw-medium">Judul Buku Tersedia</p>
            </div>
          </div>
          <div class="col-6">
            <div class="stat-landing-card text-center">
              <div class="d-inline-flex p-3 rounded-circle mb-2" style="background: #DCFCE7; color: #16A34A;">
                <i class="bi bi-layers fs-3"></i>
              </div>
              <h3 class="fw-bold mb-0 text-dark"><?= number_format(isset($stats['total_stok']) ? $stats['total_stok'] : 0) ?></h3>
              <p class="text-secondary small mb-0 fw-medium">Eksemplar Fisik di Rak</p>
            </div>
          </div>
          <div class="col-6">
            <div class="stat-landing-card text-center">
              <div class="d-inline-flex p-3 rounded-circle mb-2" style="background: #E0F2FE; color: #0284C7;">
                <i class="bi bi-people fs-3"></i>
              </div>
              <h3 class="fw-bold mb-0 text-dark"><?= number_format(isset($stats['total_anggota']) ? $stats['total_anggota'] : 0) ?></h3>
              <p class="text-secondary small mb-0 fw-medium">Anggota Terdaftar</p>
            </div>
          </div>
          <div class="col-6">
            <div class="stat-landing-card text-center">
              <div class="d-inline-flex p-3 rounded-circle mb-2" style="background: #FEF3C7; color: #D97706;">
                <i class="bi bi-journal-arrow-up fs-3"></i>
              </div>
              <h3 class="fw-bold mb-0 text-dark"><?= number_format(isset($stats['total_dipinjam']) ? $stats['total_dipinjam'] : 0) ?></h3>
              <p class="text-secondary small mb-0 fw-medium">Buku Sedang Dibaca</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

<!-- Featured Books Showcase Section -->
<section id="katalog" class="py-5" style="background: #F8FAFC;">
  <div class="container py-3">
    <div class="d-flex flex-column flex-md-row justify-content-between align-items-md-end mb-4 gap-2">
      <div>
        <span class="text-uppercase fw-bold text-primary small" style="letter-spacing: 0.05em;">KOLEKSI TERKINI</span>
        <h2 class="fw-bold text-dark mb-1">Rekomendasi Buku Bacaan</h2>
        <p class="text-secondary mb-0">Temukan bahan bacaan pilihan yang siap dipinjam di ruang perpustakaan.</p>
      </div>
      <div>
        <a href="<?= site_url('login') ?>" class="btn btn-outline-primary fw-semibold px-3">
          Semua Koleksi &rarr;
        </a>
      </div>
    </div>

    <div class="row g-4">
      <?php if (!empty($featured_books)): ?>
        <?php foreach ($featured_books as $book): ?>
          <div class="col-sm-6 col-md-4 col-lg-3">
            <div class="card h-100 border-0 shadow-sm book-card-hover bg-white">
              <div class="p-3 text-center bg-light position-relative" style="height: 190px; display: flex; align-items: center; justify-content: center;">
                <?php $cover_url = get_book_cover_url($book->gambar); if ($cover_url): ?>
                  <img src="<?= $cover_url ?>" alt="<?= html_escape($book->judul) ?>" class="rounded shadow-sm" style="max-height: 160px; max-width: 100%; object-fit: contain;">
                <?php else: ?>
                  <div class="text-muted text-center">
                    <i class="bi bi-book fs-1 text-secondary opacity-50"></i>
                    <div class="small mt-1 text-muted">Sampul Tidak Ada</div>
                  </div>
                <?php endif; ?>

                <?php if ($book->stok > 0): ?>
                  <span class="badge bg-success position-absolute top-0 end-0 m-2 fw-semibold px-2 py-1" style="font-size: 11px;">
                    Tersedia (<?= $book->stok ?>)
                  </span>
                <?php else: ?>
                  <span class="badge bg-danger position-absolute top-0 end-0 m-2 fw-semibold px-2 py-1" style="font-size: 11px;">
                    Sedang Dipinjam
                  </span>
                <?php endif; ?>
              </div>

              <div class="card-body p-3 d-flex flex-column">
                <span class="badge bg-light text-secondary border align-self-start mb-2" style="font-size: 11px;">
                  <?= html_escape($book->kategori ? $book->kategori : 'Umum') ?>
                </span>
                <h6 class="fw-bold text-dark mb-1 text-truncate" title="<?= html_escape($book->judul) ?>">
                  <?= html_escape($book->judul) ?>
                </h6>
                <p class="text-secondary small mb-3 text-truncate">
                  <?= html_escape($book->penulis ? $book->penulis : 'Penulis tidak dicantumkan') ?>
                </p>

                <div class="mt-auto pt-2 border-top d-flex justify-content-between align-items-center">
                  <span class="small text-muted font-monospace"><i class="bi bi-geo-alt me-1"></i><?= html_escape($book->rak ? $book->rak : 'Rak Umum') ?></span>
                  <a href="<?= site_url('login') ?>" class="btn btn-sm btn-primary px-3 fw-semibold text-white">
                    Pinjam
                  </a>
                </div>
              </div>
            </div>
          </div>
        <?php endforeach; ?>
      <?php else: ?>
        <div class="col-12 text-center py-5">
          <p class="text-secondary">Belum ada koleksi buku yang terdaftar.</p>
        </div>
      <?php endif; ?>
    </div>
  </div>
</section>

<!-- Alur Peminjaman Section -->
<section id="layanan" class="py-5 bg-white border-top">
  <div class="container py-3">
    <div class="text-center max-w-700 mx-auto mb-5">
      <span class="text-uppercase fw-bold text-primary small" style="letter-spacing: 0.05em;">PANDUAN LAYANAN</span>
      <h2 class="fw-bold text-dark mb-2">Cara Meminjam Buku di Perpustakaan</h2>
      <p class="text-secondary">Proses peminjaman dan pengembalian buku dirancang mudah, tertib, dan nyaman bagi seluruh siswa.</p>
    </div>

    <div class="row g-4">
      <div class="col-md-4">
        <div class="info-step-card">
          <div class="d-inline-flex p-3 rounded-circle mb-3" style="background: #FFF7ED; color: var(--primary);">
            <i class="bi bi-search fs-3"></i>
          </div>
          <div class="small fw-bold text-primary mb-1">LANGKAH 1</div>
          <h5 class="fw-bold text-dark mb-2">Cari Judul &amp; Cek Rak</h5>
          <p class="text-secondary small mb-0">
            Jelajahi katalog online untuk memeriksa ketersediaan eksemplar buku dan catat nomor rak penyimpanan buku di ruang perpustakaan.
          </p>
        </div>
      </div>
      <div class="col-md-4">
        <div class="info-step-card">
          <div class="d-inline-flex p-3 rounded-circle mb-3" style="background: #DCFCE7; color: #16A34A;">
            <i class="bi bi-journal-plus fs-3"></i>
          </div>
          <div class="small fw-bold text-success mb-1">LANGKAH 2</div>
          <h5 class="fw-bold text-dark mb-2">Pilih Buku &amp; Ajukan Pinjaman</h5>
          <p class="text-secondary small mb-0">
            Masuk ke akun anggota siswa Anda, pilih buku yang diinginkan, dan klik tombol peminjaman. Sistem akan mencatat transaksi peminjaman Anda.
          </p>
        </div>
      </div>
      <div class="col-md-4">
        <div class="info-step-card">
          <div class="d-inline-flex p-3 rounded-circle mb-3" style="background: #E0F2FE; color: #0284C7;">
            <i class="bi bi-arrow-counterclockwise fs-3"></i>
          </div>
          <div class="small fw-bold text-info mb-1">LANGKAH 3</div>
          <h5 class="fw-bold text-dark mb-2">Baca &amp; Kembalikan Tepat Waktu</h5>
          <p class="text-secondary small mb-0">
            Manfaatkan buku untuk belajar dengan baik. Setelah selesai membaca, kembalikan buku ke meja sirkulasi petugas tepat waktu agar dapat dimanfaatkan siswa lain.
          </p>
        </div>
      </div>
    </div>
  </div>
</section>

<!-- Tentang Perpustakaan Section -->
<section id="tentang" class="py-5" style="background: #FFF7ED;">
  <div class="container py-3">
    <div class="row align-items-center g-5">
      <div class="col-lg-6">
        <span class="text-uppercase fw-bold text-primary small" style="letter-spacing: 0.05em;">TENTANG KAMI</span>
        <h2 class="fw-bold text-dark mb-3">Membangun Budaya Gemar Membaca &amp; Berliterasi</h2>
        <p class="text-secondary mb-3" style="line-height: 1.7;">
          Perpustakaan SMK Ardli Sela berdedikasi menjadi pusat sumber belajar dan pusat dokumentasi informasi sekolah. Kami menyediakan fasilitas ruang baca yang tenang, koleksi buku pelajaran dan kejuruan yang komprehensif, serta pelayanan sirkulasi yang ramah.
        </p>
        <p class="text-secondary mb-4" style="line-height: 1.7;">
          Dengan sistem terkomputerisasi ini, siswa dapat dengan mudah memantau buku apa saja yang sedang dipinjam dan kapan batas waktu pengembaliannya langsung dari gawai masing-masing.
        </p>

        <div class="row g-3">
          <div class="col-sm-6">
            <div class="d-flex align-items-center gap-2">
              <i class="bi bi-clock text-primary fs-4"></i>
              <div>
                <div class="fw-bold text-dark small">Jam Operasional</div>
                <div class="text-secondary small">Senin - Jumat (07.30 - 15.30)</div>
              </div>
            </div>
          </div>
          <div class="col-sm-6">
            <div class="d-flex align-items-center gap-2">
              <i class="bi bi-geo-alt text-primary fs-4"></i>
              <div>
                <div class="fw-bold text-dark small">Lokasi Ruangan</div>
                <div class="text-secondary small">Gedung Utama Lantai 2</div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <div class="col-lg-6">
        <div class="bg-white p-4 rounded-4 shadow-sm border">
          <h5 class="fw-bold text-dark mb-3"><i class="bi bi-info-circle text-primary me-2"></i> Ketentuan Peminjaman Buku</h5>
          <ul class="list-unstyled text-secondary small mb-4" style="line-height: 2;">
            <li><i class="bi bi-check2 text-success me-2 fw-bold"></i> Setiap siswa terdaftar berhak meminjam buku perpustakaan.</li>
            <li><i class="bi bi-check2 text-success me-2 fw-bold"></i> Maksimal peminjaman aktif adalah 3 buku per siswa.</li>
            <li><i class="bi bi-check2 text-success me-2 fw-bold"></i> Durasi masa peminjaman standar adalah 7 hari kalender.</li>
            <li><i class="bi bi-check2 text-success me-2 fw-bold"></i> Jaga kebersihan dan keutuhan fisik buku yang dipinjam.</li>
          </ul>
          <div class="p-3 rounded-3" style="background: #F8FAFC; border: 1px dashed #CBD5E1;">
            <div class="small text-muted">Butuh bantuan seputar pendaftaran atau pencarian buku langka? Silakan temui petugas perpustakaan di meja sirkulasi sekolah.</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

<!-- Footer Landing -->
<footer class="footer-landing">
  <div class="container">
    <div class="row g-4 mb-4">
      <div class="col-md-6">
        <div class="d-flex align-items-center gap-2 mb-2">
          <div style="width: 32px; height: 32px; background: var(--primary); color: #FFF; border-radius: 8px; display: flex; align-items: center; justify-content: center;">
            <i class="bi bi-book-half"></i>
          </div>
          <h5 class="fw-bold text-white mb-0">PERPUSTAKAAN SMK ARDLI SELA</h5>
        </div>
        <p class="small text-secondary mb-0" style="max-width: 420px; line-height: 1.6;">
          Sistem Informasi Pelayanan Perpustakaan Sekolah. Menyediakan akses pustaka ilmu pengetahuan untuk seluruh pendidik dan peserta didik.
        </p>
      </div>
      <div class="col-md-3">
        <h6 class="fw-bold text-white mb-3">Layanan Pengguna</h6>
        <ul class="list-unstyled small mb-0">
          <li class="mb-2"><a href="<?= site_url('login') ?>" class="text-secondary text-decoration-none hover-white">Masuk ke Akun Peminjam</a></li>
          <li class="mb-2"><a href="<?= site_url('register') ?>" class="text-secondary text-decoration-none hover-white">Pendaftaran Anggota Siswa</a></li>
          <li class="mb-2"><a href="#katalog" class="text-secondary text-decoration-none hover-white">Pencarian Koleksi Buku</a></li>
          <li class="mb-2"><a href="#layanan" class="text-secondary text-decoration-none hover-white">Ketentuan &amp; Alur Peminjaman</a></li>
        </ul>
      </div>
      <div class="col-md-3">
        <h6 class="fw-bold text-white mb-3">Kontak Sekolah</h6>
        <p class="small text-secondary mb-1">
          <i class="bi bi-geo-alt me-1 text-primary"></i> Jl. Pendidikan No. 1, SMK Ardli Sela
        </p>
        <p class="small text-secondary mb-1">
          <i class="bi bi-envelope me-1 text-primary"></i> perpustakaan@ardlisela.sch.id
        </p>
        <p class="small text-secondary mb-0">
          <i class="bi bi-telephone me-1 text-primary"></i> (021) 1234567
        </p>
      </div>
    </div>

    <div class="pt-3 border-top border-secondary border-opacity-25 text-center small text-secondary">
      &copy; <?= date('Y') ?> Perpustakaan SMK Ardli Sela. Seluruh Hak Cipta Dilindungi.
    </div>
  </div>
</footer>

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

Model Pengguna (User_model.php)

Lokasi Berkas: application/models/User_model.php

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

/**
 * User_model
 * Model untuk pengelolaan data pengguna (Admin & Siswa)
 */
class User_model extends CI_Model {

    private $table = 'users';

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

    /**
     * Mengambil semua data user dengan role tertentu (default: user/siswa)
     */
    public function get_all($role = 'user', $limit = NULL, $offset = NULL) {
        $this->db->where('role', $role);
        $this->db->order_by('created_at', 'DESC');
        if ($limit !== NULL) {
            $this->db->limit($limit, $offset);
        }
        return $this->db->get($this->table)->result();
    }

    /**
     * Menghitung total data user dengan filter role
     */
    public function count_all($role = 'user', $search = NULL, $status = NULL) {
        $this->db->where('role', $role);
        if (!empty($search)) {
            $this->db->group_start();
            $this->db->like('nama', $search);
            $this->db->or_like('nis', $search);
            $this->db->or_like('username', $search);
            $this->db->group_end();
        }
        if (!empty($status)) {
            $this->db->where('status', $status);
        }
        return $this->db->count_all_results($this->table);
    }

    /**
     * Pencarian user dengan pagination & filter status
     */
    public function search($role = 'user', $search = NULL, $status = NULL, $limit = 10, $offset = 0) {
        $this->db->where('role', $role);
        if (!empty($search)) {
            $this->db->group_start();
            $this->db->like('nama', $search);
            $this->db->or_like('nis', $search);
            $this->db->or_like('username', $search);
            $this->db->group_end();
        }
        if (!empty($status)) {
            $this->db->where('status', $status);
        }
        $this->db->order_by('created_at', 'DESC');
        return $this->db->get($this->table, $limit, $offset)->result();
    }

    /**
     * Mengambil data user berdasarkan ID
     */
    public function get_by_id($id) {
        return $this->db->get_where($this->table, ['id' => $id])->row();
    }

    /**
     * Mengambil data user berdasarkan username
     */
    public function get_by_username($username) {
        return $this->db->get_where($this->table, ['username' => $username])->row();
    }

    /**
     * Menambahkan user baru
     */
    public function create($data) {
        return $this->db->insert($this->table, $data);
    }

    /**
     * Memperbarui data user
     */
    public function update($id, $data) {
        $this->db->where('id', $id);
        return $this->db->update($this->table, $data);
    }

    /**
     * Menghapus data user
     */
    public function delete($id) {
        $this->db->where('id', $id);
        return $this->db->delete($this->table);
    }

    /**
     * Cek apakah user memiliki transaksi aktif yang belum dikembalikan
     */
    public function has_active_transaction($user_id) {
        $this->db->where('user_id', $user_id);
        $this->db->where('status', 'dipinjam');
        return $this->db->count_all_results('transaksi') > 0;
    }
}
💡 Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
2

Controller Autentikasi (Auth.php)

Lokasi Berkas: application/controllers/Auth.php

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

/**
 * Auth Controller
 * Menangani Login, Registrasi Siswa, dan Logout
 */
class Auth extends CI_Controller {

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

    /**
     * Halaman Login (/login atau default /)
     */
    public function login() {
        // Jika sudah login, langsung arahkan ke dashboard masing-masing
        if (is_logged_in()) {
            if ($this->session->userdata('role') === 'admin') {
                redirect('admin/dashboard');
            } else {
                redirect('user/dashboard');
            }
        }

        // Form Validation Rules
        $this->form_validation->set_rules('username', 'Username', 'required|trim', [
            'required' => 'Username wajib diisi.'
        ]);
        $this->form_validation->set_rules('password', 'Password', 'required|trim', [
            'required' => 'Password wajib diisi.'
        ]);

        if ($this->form_validation->run() === FALSE) {
            $data['title'] = 'Masuk - SIPAS - Sistem Informasi Perpustakaan Ardli Sela';
            $this->load->view('auth/login', $data);
        } else {
            $this->_process_login();
        }
    }

    /**
     * Proses verifikasi kredensial login
     */
    private function _process_login() {
        $username = $this->input->post('username', TRUE);
        $password = $this->input->post('password', TRUE);

        $user = $this->User_model->get_by_username($username);

        // 1. Cek apakah user terdaftar
        if (!$user) {
            $this->session->set_flashdata('error', 'Username atau password salah.');
            redirect('login');
            return;
        }

        // 2. Cek status akun
        if ($user->status !== 'aktif') {
            $this->session->set_flashdata('error', 'Akun Anda sedang dinonaktifkan. Silakan hubungi petugas perpustakaan.');
            redirect('login');
            return;
        }

        // 3. Verifikasi password dengan password_verify()
        if (!password_verify($password, $user->password)) {
            $this->session->set_flashdata('error', 'Username atau password salah.');
            redirect('login');
            return;
        }

        // 4. Set Session
        $session_data = [
            'user_id'   => $user->id,
            'nama'      => $user->nama,
            'username'  => $user->username,
            'nis'       => $user->nis,
            'kelas'     => $user->kelas,
            'foto'      => !empty($user->foto) ? $user->foto : NULL,
            'role'      => $user->role,
            'logged_in' => TRUE
        ];
        $this->session->set_userdata($session_data);
        $this->session->set_flashdata('success', 'Selamat datang kembali, ' . html_escape($user->nama) . '!');

        // 5. Redirect berdasarkan role
        if ($user->role === 'admin') {
            redirect('admin/dashboard');
        } else {
            redirect('user/dashboard');
        }
    }

    /**
     * Halaman Registrasi Siswa (/register)
     */
    public function register() {
        if (is_logged_in()) {
            redirect('user/dashboard');
        }

        $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 ini sudah terdaftar.'
        ]);
        $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('konfirmasi_password', 'Konfirmasi Password', 'required|matches[password]', [
            'required' => 'Konfirmasi password wajib diisi.',
            'matches' => 'Konfirmasi password tidak cocok dengan password.'
        ]);

        if ($this->form_validation->run() === FALSE) {
            $data['title'] = 'Pendaftaran Anggota - SIPAS';
            $this->load->view('auth/register', $data);
        } else {
            $data_user = [
                '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)),
                'password'   => password_hash($this->input->post('password'), PASSWORD_BCRYPT),
                'role'       => 'user',
                'status'     => 'aktif',
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s')
            ];

            if ($this->User_model->create($data_user)) {
                $this->session->set_flashdata('success', 'Pendaftaran berhasil. Silakan masuk dengan akun Anda.');
                redirect('login');
            } else {
                $this->session->set_flashdata('error', 'Terjadi kesalahan sistem saat mendaftar. Silakan coba lagi.');
                redirect('register');
            }
        }
    }

    /**
     * Proses Logout (/logout)
     */
    public function logout() {
        $this->session->sess_destroy();
        redirect('login');
    }
}
💡 Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
3

View Formulir Login (login.php)

Lokasi Berkas: application/views/auth/login.php

Tampilan Halaman Login
Gambar: Pratinjau antarmuka halaman login SIPAS (/login).
application/views/auth/login.php PHP
<?php $this->load->view('layouts/header'); ?>

<div class="auth-wrapper">
    <div class="auth-card">
        <div class="text-center mb-4">
            <div class="sidebar-brand-icon mx-auto mb-2" style="width: 48px; height: 48px; font-size: 1.5rem;">
                <i class="bi bi-book-half"></i>
            </div>
            <h4 class="fw-bold text-dark mb-1">PERPUSTAKAAN ARDLI SELA</h4>
            <p class="text-secondary small mb-0">Masuk untuk mengakses layanan perpustakaan digital</p>
        </div>

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

        <form action="<?= site_url('login') ?>" method="POST" class="prevent-double-submit">
            <div class="mb-3">
                <label for="username" class="form-label">Username</label>
                <div class="input-group">
                    <span class="input-group-text bg-light text-muted border-end-0"><i class="bi bi-person"></i></span>
                    <input type="text" class="form-control border-start-0 ps-0" id="username" name="username" value="<?= set_value('username') ?>" placeholder="Masukkan username Anda" required autofocus>
                </div>
            </div>

            <div class="mb-4">
                <label for="password" class="form-label">Password</label>
                <div class="input-group">
                    <span class="input-group-text bg-light text-muted border-end-0"><i class="bi bi-lock"></i></span>
                    <input type="password" class="form-control border-start-0 ps-0" id="password" name="password" placeholder="Masukkan password" required>
                </div>
            </div>

            <button type="submit" class="btn btn-primary w-100 py-2 justify-content-center fw-semibold mb-3">
                <i class="bi bi-box-arrow-in-right"></i> MASUK
            </button>

            <div class="text-center">
                <span class="text-muted small">Belum punya akun?</span>
                <a href="<?= site_url('register') ?>" class="small fw-semibold text-decoration-none text-primary ms-1">Daftar Sekarang</a>
            </div>
        </form>
    </div>
</div>

<?php $this->load->view('layouts/footer'); ?>
💡 Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.
4

View Formulir Registrasi (register.php)

Lokasi Berkas: application/views/auth/register.php

Tampilan Halaman Registrasi
Gambar: Pratinjau antarmuka formulir registrasi anggota siswa (/register).
application/views/auth/register.php PHP
<?php $this->load->view('layouts/header'); ?>

<div class="auth-wrapper">
    <div class="auth-card auth-card-wide">
        <div class="text-center mb-4">
            <div class="sidebar-brand-icon mx-auto mb-2" style="width: 48px; height: 48px; font-size: 1.5rem;">
                <i class="bi bi-person-plus-fill"></i>
            </div>
            <h4 class="fw-bold text-dark mb-1">Daftar Akun Anggota</h4>
            <p class="text-secondary small mb-0">Lengkapi data diri untuk menjadi anggota perpustakaan digital</p>
        </div>

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

        <form action="<?= site_url('register') ?>" method="POST" class="prevent-double-submit">
            <div class="mb-3">
                <label for="nama" class="form-label">Nama Lengkap</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)</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</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="mb-3">
                <label for="username" class="form-label">Username</label>
                <input type="text" class="form-control" id="username" name="username" value="<?= set_value('username') ?>" placeholder="Pilih username unik (tanpa spasi)" required>
            </div>

            <div class="row g-3 mb-4">
                <div class="col-md-6">
                    <label for="password" class="form-label">Password</label>
                    <input type="password" class="form-control" id="password" name="password" placeholder="Minimal 5 karakter" required>
                </div>
                <div class="col-md-6">
                    <label for="konfirmasi_password" class="form-label">Konfirmasi Password</label>
                    <input type="password" class="form-control" id="konfirmasi_password" name="konfirmasi_password" placeholder="Ulangi password" required>
                </div>
            </div>

            <button type="submit" class="btn btn-primary w-100 py-2 justify-content-center fw-semibold mb-3">
                <i class="bi bi-check2-circle"></i> DAFTAR SEKARANG
            </button>

            <div class="text-center">
                <span class="text-muted small">Sudah punya akun?</span>
                <a href="<?= site_url('login') ?>" class="small fw-semibold text-decoration-none text-primary ms-1">Masuk</a>
            </div>
        </form>
    </div>
</div>

<?php $this->load->view('layouts/footer'); ?>
💡 Catatan: Baris komentar (// atau /* */) bersifat opsional/penjelas logika. Tidak wajib diketik semua agar lebih cepat.