Pertemuan 15 - Dashboard User

Pertemuan 15 - Dashboard User

Membangun dashboard pegawai dari update dashboard sebelumnya, lengkap dengan profile dan presensi.

Tujuan Praktikum

Setelah praktikum ini, mahasiswa mampu:

  1. Membuat tampilan dashboard untuk pegawai.
  2. Menghubungkan dashboard pegawai dengan data dari database.
  3. Menyediakan shortcut ke halaman profile dan presensi.
  4. Menyaring menu sesuai role pegawai.

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/WEB-INF/views/profile.jsp`
- File `web/WEB-INF/views/presensi.jsp`
- File `web/WEB-INF/views/partials/sidebar.jsp`
- File `src/java/controller/AuthFilter.java`
- File `web/assets/css/style.css`
- User pegawai yang sudah terhubung ke data pegawai

Konsep Singkat

Dashboard user adalah halaman utama untuk pegawai yang sedang login.

Alurnya seperti ini:

text
Login pegawai -> DashboardServlet.java -> dashboard/pegawai.jsp

Pada versi ini, DashboardServlet.java tidak hanya untuk admin, tetapi juga bisa mengarah ke dashboard pegawai.

sidebar.jsp menampilkan menu yang berbeda sesuai role.

AuthFilter.java membatasi halaman yang boleh dibuka oleh pegawai.

Langkah 1 - Update File Dashboard

Di pertemuan ini, file yang diupdate adalah:

text
src/java/controller/DashboardServlet.java
web/WEB-INF/views/dashboard/pegawai.jsp
web/WEB-INF/views/partials/sidebar.jsp
src/java/controller/AuthFilter.java

Catatan:

text
DashboardDAO, PegawaiDAO, dan PresensiDAO tetap dipakai dari alur dashboard sebelumnya.
Di langkah ini kita fokus pada file yang berubah untuk dashboard pegawai.

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

1. 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);
    }
}

2. 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>

3. web/WEB-INF/views/partials/sidebar.jsp

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

<nav id="sidebar">
    <ul>
        <li class="${requestScope.activeMenu == 'home' ? 'active' : ''}">
            <a href="${pageContext.request.contextPath}/dashboard">
                Home
            </a>
        </li>

        <c:choose>
            <c:when test="${sessionScope.userLogin.role == 'pegawai'}">
                <li class="${requestScope.activeMenu == 'profile' ? 'active' : ''}">
                    <a href="${pageContext.request.contextPath}/profile">
                        Profile
                    </a>
                </li>

                <li class="${requestScope.activeMenu == 'presensi' ? 'active' : ''}">
                    <a href="${pageContext.request.contextPath}/presensi">
                        Presensi
                    </a>
                </li>
            </c:when>

            <c:otherwise>
                <li class="${requestScope.activeMenu == 'bagian' ? 'active' : ''}">
                    <a href="${pageContext.request.contextPath}/bagian">
                        Entry Data Bagian
                    </a>
                </li>

                <li class="${requestScope.activeMenu == 'pegawai' ? 'active' : ''}">
                    <a href="${pageContext.request.contextPath}/pegawai">
                        Entry Data Pegawai
                    </a>
                </li>

                <li class="${requestScope.activeMenu == 'users' ? 'active' : ''}">
                    <a href="${pageContext.request.contextPath}/users">
                        Manajemen User
                    </a>
                </li>
            </c:otherwise>
        </c:choose>
    </ul>
</nav>

4. src/java/controller/AuthFilter.java

java
package controller;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import model.User;

import java.io.IOException;

/**
 * Filter untuk memastikan semua halaman hanya bisa diakses
 * oleh user yang sudah login.
 *
 * Pengecualian (URL yang boleh diakses tanpa login):
 *   - /login
 *   - /assets/* (CSS, JS, gambar)
 */
@WebFilter(filterName = "AuthFilter", urlPatterns = {"/*"})
public class AuthFilter implements Filter {

    // URL yang boleh diakses tanpa login
    private static final String[] PUBLIC_URLS = {
        "/login",
        "/assets/",
        "/db-status"
    };

    private static final String[] PEGAWAI_URLS = {
        "/",
        "/index.jsp",
        "/dashboard",
        "/profile",
        "/presensi",
        "/logout",
        "/db-status"
    };

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Tidak ada inisialisasi khusus
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {

        HttpServletRequest  httpReq  = (HttpServletRequest)  request;
        HttpServletResponse httpRes  = (HttpServletResponse) response;

        String requestURI = httpReq.getRequestURI();
        String contextPath = httpReq.getContextPath();

        // Cek apakah URL ini termasuk public (tidak butuh login)
        String pathWithoutContext = requestURI.substring(contextPath.length());
        for (String publicUrl : PUBLIC_URLS) {
            if (pathWithoutContext.startsWith(publicUrl)) {
                chain.doFilter(request, response); // lolos tanpa cek session
                return;
            }
        }

        HttpSession session = httpReq.getSession(false);
        boolean sudahLogin  = (session != null)
                           && (session.getAttribute("userLogin") != null);

        if (!sudahLogin) {
            httpRes.sendRedirect(contextPath + "/login");
            return;
        }

        User user = (User) session.getAttribute("userLogin");
        if (user != null && user.isPegawai() && !isPegawaiUrl(pathWithoutContext)) {
            httpRes.sendRedirect(contextPath + "/index.jsp");
            return;
        }

        chain.doFilter(request, response);
    }

    private boolean isPegawaiUrl(String path) {
        for (String allowedPath : PEGAWAI_URLS) {
            if ("/".equals(allowedPath) && "/".equals(path)) {
                return true;
            }

            if (!"/".equals(allowedPath) && path.startsWith(allowedPath)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void destroy() {
        // Cleanup jika diperlukan
    }
}

Langkah 2 - Pastikan Integrasi Role Pegawai

Setelah file dashboard pegawai ditulis, cek bahwa alur role pegawai benar-benar tersambung.

Hal yang perlu dicek:

text
- dashboard pegawai menampilkan nama, bagian, dan ringkasan presensi
- menu sidebar untuk pegawai hanya `Home`, `Profile`, dan `Presensi`
- tombol shortcut mengarah ke `/profile` dan `/presensi`
- `AuthFilter.java` membatasi URL sesuai role pegawai
- `web/index.jsp` tetap menjadi layout utama

Jika tampilan kartu atau grafik perlu penyesuaian, update juga web/assets/css/style.css.

Pengujian

Lakukan pengujian berikut:

  1. Login sebagai pegawai.
  2. Buka /dashboard.
  3. Pastikan dashboard pegawai tampil.
  4. Cek apakah nama dan bagian pegawai muncul.
  5. Coba buka menu admin, lalu lihat apakah akses ditolak.

Troubleshooting

Masalah Penyebab Solusi
Dashboard tidak sesuai role User login bukan pegawai Login menggunakan akun pegawai
Nama pegawai tidak tampil User belum terhubung ke data pegawai Hubungkan akun ke pegawai terlebih dahulu
Menu profile dan presensi tidak muncul Sidebar membaca role yang salah Pastikan session user berisi role pegawai
Akses ke menu lain ditolak AuthFilter membatasi URL Gunakan URL yang sesuai dengan role

Tugas Praktikum

  1. Login sebagai pegawai lalu screenshot dashboard.
  2. Buka halaman profile dari dashboard.
  3. Buka halaman presensi dari dashboard.

Kesimpulan

Dashboard user adalah versi dashboard yang khusus untuk pegawai. Satu servlet yang sama bisa dipakai untuk admin dan pegawai, tetapi view, menu, dan data yang ditampilkan berbeda sesuai role.