Pertemuan 14 - Dashboard Admin

Pertemuan 14 - Dashboard Admin

Membangun dashboard admin dari controller, DAO, view, dan layout utama aplikasi.

Tujuan Praktikum

Setelah praktikum ini, mahasiswa mampu:

  1. Menjelaskan alur dashboard admin pada aplikasi.
  2. Membuat file controller, DAO, dan view untuk dashboard.
  3. Menghubungkan dashboard ke layout utama index.jsp.
  4. Menampilkan menu admin dari sidebar.jsp.
  5. Menguji tampilan dashboard dari browser.

Persiapan

Siapkan kebutuhan berikut:

text
- Project Java Web Starter
- Folder `src/java/controller`
- Folder `src/java/dao`
- Folder `src/java/model`
- Folder `web/WEB-INF/views/dashboard`
- File `web/index.jsp`
- File `web/WEB-INF/views/partials/header.jsp`
- File `web/WEB-INF/views/partials/sidebar.jsp`
- File `web/WEB-INF/views/partials/footer.jsp`
- File `web/assets/css/style.css`
- Data bagian, pegawai, user, dan presensi di database

Konsep Singkat

Dashboard admin adalah halaman ringkasan untuk user dengan role admin.

Alurnya seperti ini:

text
/dashboard -> DashboardServlet.java -> DashboardDAO.java -> dashboard/admin.jsp

DashboardServlet.java menerima request dari browser lalu menyiapkan data untuk view.

DashboardDAO.java mengambil data ringkasan dari database.

dashboard/admin.jsp menampilkan hasilnya dalam bentuk kartu dan grafik.

index.jsp dipakai sebagai layout utama, sedangkan sidebar.jsp menampilkan menu admin.

Jika tampilan butuh penyesuaian, update juga web/assets/css/style.css.

Langkah 1 - Buat Folder dan File Dasar

Buat file berikut sesuai urutan ini:

text
src/java/model/ChartItem.java
src/java/dao/DashboardDAO.java
src/java/controller/DashboardServlet.java
web/WEB-INF/views/dashboard/admin.jsp
web/WEB-INF/views/dashboard/pegawai.jsp

Kalau folder dashboard belum ada, buat dulu di dalam web/WEB-INF/views/.

1. src/java/model/ChartItem.java

java
package model;

public class ChartItem {

    private String label;
    private int value;
    private int percent;

    public ChartItem() {
    }

    public ChartItem(String label, int value, int percent) {
        this.label = label;
        this.value = value;
        this.percent = percent;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public int getPercent() {
        return percent;
    }

    public void setPercent(int percent) {
        this.percent = percent;
    }
}

2. src/java/dao/DashboardDAO.java

java
package dao;

import model.ChartItem;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class DashboardDAO {

    public int countBagian() throws SQLException {
        return count("SELECT COUNT(*) FROM bagian");
    }

    public int countPegawaiAktif() throws SQLException {
        return count("SELECT COUNT(*) FROM pegawai WHERE COALESCE(status_pegawai, 'Aktif') = 'Aktif'");
    }

    public int countUserAktif() throws SQLException {
        return count("SELECT COUNT(*) FROM users WHERE COALESCE(status_user, 'Aktif') = 'Aktif'");
    }

    public int countPresensiDatangHariIni() throws SQLException {
        return count("SELECT COUNT(*) FROM presensi WHERE tanggal = CURDATE() AND jam_masuk IS NOT NULL");
    }

    public int countPresensiPulangHariIni() throws SQLException {
        return count("SELECT COUNT(*) FROM presensi WHERE tanggal = CURDATE() AND jam_pulang IS NOT NULL");
    }

    public int countPresensiBelumHariIni() throws SQLException {
        return Math.max(0, countPegawaiAktif() - countPresensiDatangHariIni());
    }

    public int countPresensiBulanIni(int pegawaiId) throws SQLException {
        String sql = "SELECT COUNT(*) FROM presensi "
                   + "WHERE id_pegawai = ? "
                   + "AND YEAR(tanggal) = YEAR(CURDATE()) "
                   + "AND MONTH(tanggal) = MONTH(CURDATE()) "
                   + "AND jam_masuk IS NOT NULL";

        return countByPegawai(sql, pegawaiId);
    }

    public int countPulangBulanIni(int pegawaiId) throws SQLException {
        String sql = "SELECT COUNT(*) FROM presensi "
                   + "WHERE id_pegawai = ? "
                   + "AND YEAR(tanggal) = YEAR(CURDATE()) "
                   + "AND MONTH(tanggal) = MONTH(CURDATE()) "
                   + "AND jam_pulang IS NOT NULL";

        return countByPegawai(sql, pegawaiId);
    }

    public List<ChartItem> findPegawaiByBagian() throws SQLException {
        String sql = "SELECT b.nama_bagian AS label, COUNT(p.id_pegawai) AS total "
                   + "FROM bagian b "
                   + "LEFT JOIN pegawai p ON p.id_bagian = b.id_bagian "
                   + "GROUP BY b.id_bagian, b.nama_bagian "
                   + "ORDER BY total DESC, b.nama_bagian ASC";

        return findChartItems(sql);
    }

    public List<ChartItem> findPresensiLastSevenDays() throws SQLException {
        String sql = "SELECT DATE_FORMAT(tanggal, '%d/%m') AS label, COUNT(*) AS total "
                   + "FROM presensi "
                   + "WHERE tanggal >= DATE_SUB(CURDATE(), INTERVAL 6 DAY) "
                   + "AND jam_masuk IS NOT NULL "
                   + "GROUP BY tanggal "
                   + "ORDER BY tanggal ASC";

        return findChartItems(sql);
    }

    public List<ChartItem> findAdminTodayStatus() throws SQLException {
        List<ChartItem> items = new ArrayList<>();
        int datang = countPresensiDatangHariIni();
        int pulang = countPresensiPulangHariIni();
        int belum = countPresensiBelumHariIni();
        int max = Math.max(1, Math.max(datang, Math.max(pulang, belum)));

        items.add(new ChartItem("Sudah Datang", datang, toPercent(datang, max)));
        items.add(new ChartItem("Sudah Pulang", pulang, toPercent(pulang, max)));
        items.add(new ChartItem("Belum Presensi", belum, toPercent(belum, max)));
        return items;
    }

    public List<ChartItem> findPegawaiMonthStatus(int pegawaiId) throws SQLException {
        List<ChartItem> items = new ArrayList<>();
        int hadir = countPresensiBulanIni(pegawaiId);
        int pulang = countPulangBulanIni(pegawaiId);
        int belumPulang = Math.max(0, hadir - pulang);
        int max = Math.max(1, Math.max(hadir, Math.max(pulang, belumPulang)));

        items.add(new ChartItem("Hadir Bulan Ini", hadir, toPercent(hadir, max)));
        items.add(new ChartItem("Pulang Tercatat", pulang, toPercent(pulang, max)));
        items.add(new ChartItem("Belum Pulang", belumPulang, toPercent(belumPulang, max)));
        return items;
    }

    public List<ChartItem> findPegawaiLastSevenDays(int pegawaiId) throws SQLException {
        String sql = "SELECT DATE_FORMAT(tanggal, '%d/%m') AS label, COUNT(*) AS total "
                   + "FROM presensi "
                   + "WHERE id_pegawai = ? "
                   + "AND tanggal >= DATE_SUB(CURDATE(), INTERVAL 6 DAY) "
                   + "AND jam_masuk IS NOT NULL "
                   + "GROUP BY tanggal "
                   + "ORDER BY tanggal ASC";

        List<ChartItem> items = new ArrayList<>();

        try (Connection conn = DBKoneksi.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {

            ps.setInt(1, pegawaiId);

            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    int total = rs.getInt("total");
                    items.add(new ChartItem(rs.getString("label"), total, total > 0 ? 100 : 0));
                }
            }
        }

        return items;
    }

    private int count(String sql) throws SQLException {
        try (Connection conn = DBKoneksi.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql);
             ResultSet rs = ps.executeQuery()) {

            if (rs.next()) {
                return rs.getInt(1);
            }
        }

        return 0;
    }

    private int countByPegawai(String sql, int pegawaiId) throws SQLException {
        try (Connection conn = DBKoneksi.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {

            ps.setInt(1, pegawaiId);

            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    return rs.getInt(1);
                }
            }
        }

        return 0;
    }

    private List<ChartItem> findChartItems(String sql) throws SQLException {
        List<ChartItem> items = new ArrayList<>();
        int max = 1;

        try (Connection conn = DBKoneksi.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql);
             ResultSet rs = ps.executeQuery()) {

            while (rs.next()) {
                int total = rs.getInt("total");
                max = Math.max(max, total);
                items.add(new ChartItem(rs.getString("label"), total, 0));
            }
        }

        for (ChartItem item : items) {
            item.setPercent(toPercent(item.getValue(), max));
        }

        return items;
    }

    private int toPercent(int value, int max) {
        if (value <= 0 || max <= 0) {
            return 0;
        }

        return Math.max(6, Math.round((value * 100f) / max));
    }
}

3. src/java/controller/DashboardServlet.java

java
package controller;

import dao.DashboardDAO;
import dao.PegawaiDAO;
import dao.PresensiDAO;
import model.Pegawai;
import model.Presensi;
import model.User;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;
import java.sql.SQLException;

@WebServlet(name = "DashboardServlet", urlPatterns = {"/dashboard"})
public class DashboardServlet extends HttpServlet {

    private static final String ADMIN_PAGE = "/WEB-INF/views/dashboard/admin.jsp";
    private static final String PEGAWAI_PAGE = "/WEB-INF/views/dashboard/pegawai.jsp";

    private final DashboardDAO dashboardDAO = new DashboardDAO();
    private final PegawaiDAO pegawaiDAO = new PegawaiDAO();
    private final PresensiDAO presensiDAO = new PresensiDAO();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        User user = getUserLogin(request);
        request.setAttribute("activeMenu", "home");

        try {
            if (user != null && user.isPegawai()) {
                showPegawaiDashboard(request, response, user);
                return;
            }

            showAdminDashboard(request, response);
        } catch (SQLException e) {
            throw new ServletException("Gagal membuka dashboard.", e);
        }
    }

    private void showAdminDashboard(HttpServletRequest request, HttpServletResponse response)
            throws SQLException, ServletException, IOException {

        request.setAttribute("totalPegawaiAktif", dashboardDAO.countPegawaiAktif());
        request.setAttribute("totalBagian", dashboardDAO.countBagian());
        request.setAttribute("totalUserAktif", dashboardDAO.countUserAktif());
        request.setAttribute("totalDatangHariIni", dashboardDAO.countPresensiDatangHariIni());
        request.setAttribute("totalPulangHariIni", dashboardDAO.countPresensiPulangHariIni());
        request.setAttribute("totalBelumPresensiHariIni", dashboardDAO.countPresensiBelumHariIni());
        request.setAttribute("pegawaiByBagian", dashboardDAO.findPegawaiByBagian());
        request.setAttribute("presensiLastSevenDays", dashboardDAO.findPresensiLastSevenDays());
        request.setAttribute("presensiTodayStatus", dashboardDAO.findAdminTodayStatus());
        forwardToLayout(request, response, ADMIN_PAGE);
    }

    private void showPegawaiDashboard(HttpServletRequest request, HttpServletResponse response, User user)
            throws SQLException, ServletException, IOException {

        if (user.getPegawaiId() == null || user.getPegawaiId() <= 0) {
            request.setAttribute("message", "Akun ini belum terhubung ke data pegawai.");
            forwardToLayout(request, response, PEGAWAI_PAGE);
            return;
        }

        Pegawai pegawai = pegawaiDAO.findById(user.getPegawaiId());
        Presensi presensiHariIni = presensiDAO.findTodayByPegawaiId(user.getPegawaiId());

        request.setAttribute("pegawai", pegawai);
        request.setAttribute("presensiHariIni", presensiHariIni);
        request.setAttribute("hadirBulanIni", dashboardDAO.countPresensiBulanIni(user.getPegawaiId()));
        request.setAttribute("pulangBulanIni", dashboardDAO.countPulangBulanIni(user.getPegawaiId()));
        request.setAttribute("presensiMonthStatus", dashboardDAO.findPegawaiMonthStatus(user.getPegawaiId()));
        request.setAttribute("presensiLastSevenDays", dashboardDAO.findPegawaiLastSevenDays(user.getPegawaiId()));
        forwardToLayout(request, response, PEGAWAI_PAGE);
    }

    private User getUserLogin(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return null;
        }

        Object user = session.getAttribute("userLogin");
        return user instanceof User ? (User) user : null;
    }

    private void forwardToLayout(HttpServletRequest request, HttpServletResponse response,
                                 String contentPage) throws ServletException, IOException {
        request.setAttribute("contentPage", contentPage);
        request.getRequestDispatcher("/index.jsp").forward(request, response);
    }
}

4. web/WEB-INF/views/dashboard/admin.jsp

jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<div class="page-header">
    <h2>Dashboard Admin</h2>
</div>

<div class="stats-grid">
    <div class="stat-card">
        <span>Pegawai Aktif</span>
        <strong>${totalPegawaiAktif}</strong>
    </div>
    <div class="stat-card">
        <span>Bagian</span>
        <strong>${totalBagian}</strong>
    </div>
    <div class="stat-card">
        <span>User Aktif</span>
        <strong>${totalUserAktif}</strong>
    </div>
    <div class="stat-card">
        <span>Datang Hari Ini</span>
        <strong>${totalDatangHariIni}</strong>
    </div>
</div>

<div class="dashboard-grid">
    <div class="card">
        <h3>Status Presensi Hari Ini</h3>
        <div class="chart-list">
            <c:forEach var="item" items="${presensiTodayStatus}">
                <div class="chart-row">
                    <div class="chart-row__meta">
                        <span><c:out value="${item.label}"/></span>
                        <strong>${item.value}</strong>
                    </div>
                    <div class="chart-track">
                        <div class="chart-fill chart-fill--green" style="width: ${item.percent}%"></div>
                    </div>
                </div>
            </c:forEach>
        </div>
    </div>

    <div class="card">
        <h3>Pegawai per Bagian</h3>
        <c:choose>
            <c:when test="${empty pegawaiByBagian}">
                <div class="info">Belum ada data bagian.</div>
            </c:when>
            <c:otherwise>
                <div class="chart-list">
                    <c:forEach var="item" items="${pegawaiByBagian}">
                        <div class="chart-row">
                            <div class="chart-row__meta">
                                <span><c:out value="${item.label}"/></span>
                                <strong>${item.value}</strong>
                            </div>
                            <div class="chart-track">
                                <div class="chart-fill" style="width: ${item.percent}%"></div>
                            </div>
                        </div>
                    </c:forEach>
                </div>
            </c:otherwise>
        </c:choose>
    </div>
</div>

<div class="card">
    <h3>Tren Presensi 7 Hari Terakhir</h3>
    <c:choose>
        <c:when test="${empty presensiLastSevenDays}">
            <div class="info">Belum ada data presensi 7 hari terakhir.</div>
        </c:when>
        <c:otherwise>
            <div class="bar-chart">
                <c:forEach var="item" items="${presensiLastSevenDays}">
                    <div class="bar-chart__item">
                        <div class="bar-chart__bar" style="height: ${item.percent}%">
                            <span>${item.value}</span>
                        </div>
                        <small><c:out value="${item.label}"/></small>
                    </div>
                </c:forEach>
            </div>
        </c:otherwise>
    </c:choose>
</div>

5. web/WEB-INF/views/dashboard/pegawai.jsp

jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<div class="page-header">
    <h2>Dashboard Pegawai</h2>
</div>

<c:if test="${not empty message}">
    <div class="info"><c:out value="${message}"/></div>
</c:if>

<c:if test="${not empty pegawai}">
    <div class="stats-grid">
        <div class="stat-card">
            <span>Nama Pegawai</span>
            <strong><c:out value="${pegawai.namaPegawai}"/></strong>
        </div>
        <div class="stat-card">
            <span>Bagian</span>
            <strong><c:out value="${pegawai.bagianNama}"/></strong>
        </div>
        <div class="stat-card">
            <span>Hadir Bulan Ini</span>
            <strong>${hadirBulanIni}</strong>
        </div>
        <div class="stat-card">
            <span>Pulang Tercatat</span>
            <strong>${pulangBulanIni}</strong>
        </div>
    </div>

    <div class="dashboard-grid">
        <div class="card">
            <h3>Presensi Hari Ini</h3>
            <div class="presensi-status">
                <div>
                    <span>Jam Datang</span>
                    <strong><c:out value="${presensiHariIni.jamMasuk}" default="-"/></strong>
                </div>
                <div>
                    <span>Jam Pulang</span>
                    <strong><c:out value="${presensiHariIni.jamPulang}" default="-"/></strong>
                </div>
            </div>
            <div class="quick-actions">
                <a href="${pageContext.request.contextPath}/presensi" class="btn btn-add">Buka Presensi</a>
                <a href="${pageContext.request.contextPath}/profile" class="btn btn-secondary">Profile</a>
            </div>
        </div>

        <div class="card">
            <h3>Ringkasan Bulan Ini</h3>
            <div class="chart-list">
                <c:forEach var="item" items="${presensiMonthStatus}">
                    <div class="chart-row">
                        <div class="chart-row__meta">
                            <span><c:out value="${item.label}"/></span>
                            <strong>${item.value}</strong>
                        </div>
                        <div class="chart-track">
                            <div class="chart-fill chart-fill--green" style="width: ${item.percent}%"></div>
                        </div>
                    </div>
                </c:forEach>
            </div>
        </div>
    </div>

    <div class="card">
        <h3>Presensi 7 Hari Terakhir</h3>
        <c:choose>
            <c:when test="${empty presensiLastSevenDays}">
                <div class="info">Belum ada data presensi 7 hari terakhir.</div>
            </c:when>
            <c:otherwise>
                <div class="bar-chart">
                    <c:forEach var="item" items="${presensiLastSevenDays}">
                        <div class="bar-chart__item">
                            <div class="bar-chart__bar" style="height: ${item.percent}%">
                                <span>${item.value}</span>
                            </div>
                            <small><c:out value="${item.label}"/></small>
                        </div>
                    </c:forEach>
                </div>
            </c:otherwise>
        </c:choose>
    </div>
</c:if>

Perhatikan urutan file di atas. Kode dibaca dari model, lalu DAO, lalu controller, lalu view.

Langkah 2 - Sambungkan ke Layout Utama

Setelah file utama selesai dibuat, pastikan dashboard masuk ke layout yang sudah ada.

Hal yang perlu dicek:

text
- `web/index.jsp` memanggil `contentPage` dari servlet
- `web/WEB-INF/views/partials/sidebar.jsp` menampilkan menu admin
- `web/assets/css/style.css` mendukung kartu, grafik, dan sidebar

Untuk dashboard admin, menu yang muncul adalah:

text
- Home
- Entry Data Bagian
- Entry Data Pegawai
- Manajemen User

Jika tampilan dashboard belum rapi, sesuaikan class CSS yang dipakai di admin.jsp dan pegawai.jsp.

Pengujian

Lakukan pengujian berikut:

  1. Login sebagai admin.
  2. Buka /dashboard.
  3. Pastikan kartu dan grafik tampil.
  4. Ubah data di database, lalu refresh halaman.
  5. Pastikan menu Home terlihat aktif.

Troubleshooting

Masalah Penyebab Solusi
Halaman tidak tampil Route /dashboard belum dibuat Periksa DashboardServlet.java
Data angka kosong Database belum berisi data Isi bagian, pegawai, user, dan presensi
Grafik tidak muncul Atribut request belum dikirim Periksa isi DashboardServlet
Tampilan berantakan Class CSS belum ada Update style.css

Tugas Praktikum

  1. Buat dashboard admin sesuai struktur file di atas.
  2. Tambahkan data di database lalu lihat perubahan angka.
  3. Catat file apa saja yang terlibat dalam dashboard.

Kesimpulan

Dashboard admin dibangun dari controller, DAO, view, dan layout utama. Data ringkasan diambil dari database lalu ditampilkan sebagai kartu dan grafik.