Tujuan Praktikum
Setelah praktikum ini, mahasiswa mampu:
-
Menambahkan menu
Presensiuntuk rolepegawai. -
Membuat model
Presensiuntuk data presensi harian. -
Membuat
PresensiDAOuntuk membaca dan menyimpan presensi datang maupun pulang. -
Membuat
PresensiServletuntuk menampilkan halaman presensi dan memproses aksi tombol. - Menampilkan halaman JSP presensi beserta riwayat presensi terbaru.
- Menghubungkan menu presensi dengan dashboard pegawai.
Persiapan
Siapkan kebutuhan berikut:
- Project Java Web Starter
- Folder `src/java/controller`
- Folder `src/java/dao`
- Folder `src/java/model`
- Folder `web/WEB-INF/views`
- File `src/java/model/User.java`
- File `src/java/dao/UserDAO.java`
- File `src/java/dao/PegawaiDAO.java`
- File `web/WEB-INF/views/partials/sidebar.jsp`
- File `web/WEB-INF/views/dashboard/pegawai.jsp`
- Tabel `presensi` pada database
Konsep Singkat
Fitur presensi pegawai dipakai oleh user dengan role
pegawai untuk mencatat jam datang dan jam pulang pada
hari berjalan.
Alur sederhananya seperti ini:
Sidebar Pegawai
-> /presensi
-> PresensiServlet.java
-> PegawaiDAO.java
-> PresensiDAO.java
-> tabel presensi
-> presensi.jsp
Di sisi role, menu ini hanya muncul untuk pegawai:
pegawai -> Home, Profile, Presensi
admin -> Home, Entry Data Bagian, Entry Data Pegawai, Manajemen User
Logika utama fitur presensi:
- Sistem membaca user login dari session.
-
Sistem memastikan user adalah
pegawaidan punyaid_pegawai. - Sistem membaca data pegawai dan presensi hari ini.
-
Jika user klik
Datang, sistem menyimpanjam_masuk. -
Jika user klik
Pulang, sistem menyimpanjam_pulang. - Sistem menampilkan riwayat presensi terbaru.
Struktur File yang Dibutuhkan
Untuk membentuk menu presensi pegawai, file yang dipakai adalah:
src/java/model/Presensi.java
src/java/dao/PresensiDAO.java
src/java/controller/PresensiServlet.java
web/WEB-INF/views/presensi.jsp
web/WEB-INF/views/partials/sidebar.jsp
web/WEB-INF/views/dashboard/pegawai.jsp
src/java/dao/PegawaiDAO.java
src/java/model/Pegawai.java
src/java/model/User.java
Peran tiap file:
Presensi.java= model data presensi.-
PresensiDAO.java= query presensi datang, pulang, dan riwayat. -
PresensiServlet.java= controller untuk halaman dan aksi presensi. presensi.jsp= tampilan halaman presensi.-
sidebar.jsp= menuPresensiuntuk pegawai. -
dashboard/pegawai.jsp= shortcut dari dashboard ke halaman presensi.
Langkah 1 - Buat Model Presensi
Buat file src/java/model/Presensi.java.
Model ini menyimpan data utama dari tabel presensi.
package model;
public class Presensi {
private int id;
private int pegawaiId;
private String tanggal;
private String jamMasuk;
private String jamPulang;
private String statusPresensi;
private String keterangan;
public Presensi() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getPegawaiId() {
return pegawaiId;
}
public void setPegawaiId(int pegawaiId) {
this.pegawaiId = pegawaiId;
}
public String getTanggal() {
return tanggal;
}
public void setTanggal(String tanggal) {
this.tanggal = tanggal;
}
public String getJamMasuk() {
return jamMasuk;
}
public void setJamMasuk(String jamMasuk) {
this.jamMasuk = jamMasuk;
}
public String getJamPulang() {
return jamPulang;
}
public void setJamPulang(String jamPulang) {
this.jamPulang = jamPulang;
}
public String getStatusPresensi() {
return statusPresensi;
}
public void setStatusPresensi(String statusPresensi) {
this.statusPresensi = statusPresensi;
}
public String getKeterangan() {
return keterangan;
}
public void setKeterangan(String keterangan) {
this.keterangan = keterangan;
}
}
Langkah 2 - Buat DAO Presensi
Buat file src/java/dao/PresensiDAO.java.
DAO ini dipakai untuk:
- mencari presensi hari ini,
- membaca riwayat presensi terbaru,
- menyimpan presensi datang,
- menyimpan presensi pulang.
package dao;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import koneksi.DBKoneksi;
import model.Presensi;
public class PresensiDAO {
public Presensi findTodayByPegawaiId(int pegawaiId) throws SQLException {
String sql = "SELECT id_presensi AS id, id_pegawai, tanggal, jam_masuk, jam_pulang, "
+ "status_presensi, keterangan "
+ "FROM presensi "
+ "WHERE id_pegawai = ? AND tanggal = CURDATE() "
+ "ORDER BY id_presensi DESC "
+ "LIMIT 1";
try (PreparedStatement ps = DBKoneksi.getConnection().prepareStatement(sql)) {
ps.setInt(1, pegawaiId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return mapRow(rs);
}
}
}
return null;
}
public List<Presensi> findRecentByPegawaiId(int pegawaiId, int limit) throws SQLException {
String sql = "SELECT id_presensi AS id, id_pegawai, tanggal, jam_masuk, jam_pulang, "
+ "status_presensi, keterangan "
+ "FROM presensi "
+ "WHERE id_pegawai = ? "
+ "ORDER BY tanggal DESC, id_presensi DESC "
+ "LIMIT ?";
List<Presensi> presensiList = new ArrayList<>();
try (PreparedStatement ps = DBKoneksi.getConnection().prepareStatement(sql)) {
ps.setInt(1, pegawaiId);
ps.setInt(2, limit);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
presensiList.add(mapRow(rs));
}
}
}
return presensiList;
}
public void insertDatang(int pegawaiId) throws SQLException {
String sql = "INSERT INTO presensi (id_pegawai, tanggal, jam_masuk, status_presensi) "
+ "VALUES (?, CURDATE(), CURTIME(), 'Hadir')";
try (PreparedStatement ps = DBKoneksi.getConnection().prepareStatement(sql)) {
ps.setInt(1, pegawaiId);
ps.executeUpdate();
}
}
public void updateDatang(int presensiId) throws SQLException {
String sql = "UPDATE presensi SET jam_masuk = CURTIME(), status_presensi = 'Hadir' "
+ "WHERE id_presensi = ?";
try (PreparedStatement ps = DBKoneksi.getConnection().prepareStatement(sql)) {
ps.setInt(1, presensiId);
ps.executeUpdate();
}
}
public void updatePulang(int presensiId) throws SQLException {
String sql = "UPDATE presensi SET jam_pulang = CURTIME() WHERE id_presensi = ?";
try (PreparedStatement ps = DBKoneksi.getConnection().prepareStatement(sql)) {
ps.setInt(1, presensiId);
ps.executeUpdate();
}
}
private Presensi mapRow(ResultSet rs) throws SQLException {
Presensi presensi = new Presensi();
presensi.setId(rs.getInt("id"));
presensi.setPegawaiId(rs.getInt("id_pegawai"));
presensi.setTanggal(rs.getString("tanggal"));
presensi.setJamMasuk(rs.getString("jam_masuk"));
presensi.setJamPulang(rs.getString("jam_pulang"));
presensi.setStatusPresensi(rs.getString("status_presensi"));
presensi.setKeterangan(rs.getString("keterangan"));
return presensi;
}
}
Langkah 3 - Buat Servlet Presensi
Buat file src/java/controller/PresensiServlet.java.
Servlet ini menjadi pusat alur menu presensi:
doGet()untuk membuka halaman presensi,-
doPost()untuk memproses tombol datang atau pulang, - validasi role
pegawai, - membaca user login dari session.
package controller;
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.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
@WebServlet(name = "PresensiServlet", urlPatterns = {"/presensi"})
public class PresensiServlet extends HttpServlet {
private static final String PRESENSI_PAGE = "/WEB-INF/views/presensi.jsp";
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);
if (!isPegawaiWithData(user)) {
response.sendRedirect(request.getContextPath() + "/index.jsp");
return;
}
try {
Pegawai pegawai = pegawaiDAO.findById(user.getPegawaiId());
Presensi presensiHariIni = presensiDAO.findTodayByPegawaiId(user.getPegawaiId());
request.setAttribute("activeMenu", "presensi");
request.setAttribute("pegawai", pegawai);
request.setAttribute("presensiHariIni", presensiHariIni);
request.setAttribute("presensiList", presensiDAO.findRecentByPegawaiId(user.getPegawaiId(), 10));
forwardToLayout(request, response);
} catch (SQLException e) {
throw new ServletException("Gagal membuka halaman presensi.", e);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
User user = getUserLogin(request);
if (!isPegawaiWithData(user)) {
response.sendRedirect(request.getContextPath() + "/index.jsp");
return;
}
String action = request.getParameter("action");
try {
if ("datang".equals(action)) {
handleDatang(user.getPegawaiId(), request, response);
} else if ("pulang".equals(action)) {
handlePulang(user.getPegawaiId(), request, response);
} else {
redirectWithMessage(response, request, "Aksi presensi tidak valid.", "error");
}
} catch (SQLException e) {
redirectWithMessage(response, request, "Gagal menyimpan presensi.", "error");
}
}
private void handleDatang(int pegawaiId, HttpServletRequest request, HttpServletResponse response)
throws SQLException, IOException {
Presensi hariIni = presensiDAO.findTodayByPegawaiId(pegawaiId);
if (hariIni == null) {
presensiDAO.insertDatang(pegawaiId);
redirectWithMessage(response, request, "Presensi datang berhasil disimpan.", "success");
return;
}
if (hariIni.getJamMasuk() == null || hariIni.getJamMasuk().isBlank()) {
presensiDAO.updateDatang(hariIni.getId());
redirectWithMessage(response, request, "Presensi datang berhasil diperbarui.", "success");
return;
}
redirectWithMessage(response, request, "Presensi datang hari ini sudah tercatat.", "info");
}
private void handlePulang(int pegawaiId, HttpServletRequest request, HttpServletResponse response)
throws SQLException, IOException {
Presensi hariIni = presensiDAO.findTodayByPegawaiId(pegawaiId);
if (hariIni == null || hariIni.getJamMasuk() == null || hariIni.getJamMasuk().isBlank()) {
redirectWithMessage(response, request, "Klik Datang terlebih dahulu sebelum Pulang.", "error");
return;
}
if (hariIni.getJamPulang() == null || hariIni.getJamPulang().isBlank()) {
presensiDAO.updatePulang(hariIni.getId());
redirectWithMessage(response, request, "Presensi pulang berhasil disimpan.", "success");
return;
}
redirectWithMessage(response, request, "Presensi pulang hari ini sudah tercatat.", "info");
}
private boolean isPegawaiWithData(User user) {
return user != null && user.isPegawai() && user.getPegawaiId() != null && user.getPegawaiId() > 0;
}
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)
throws ServletException, IOException {
request.setAttribute("contentPage", PRESENSI_PAGE);
request.getRequestDispatcher("/index.jsp").forward(request, response);
}
private void redirectWithMessage(HttpServletResponse response, HttpServletRequest request,
String message, String type) throws IOException {
String target = request.getContextPath() + "/presensi"
+ "?message=" + URLEncoder.encode(message, StandardCharsets.UTF_8)
+ "&type=" + URLEncoder.encode(type, StandardCharsets.UTF_8);
response.sendRedirect(target);
}
}
Catatan penting dari servlet ini:
-
@WebServlet(urlPatterns = {"/presensi"})membuat URL menu presensi aktif di/presensi. -
request.setAttribute("activeMenu", "presensi")dipakai agar sidebar menandai menu aktif. -
findTodayByPegawaiId()dipakai untuk mengetahui apakah user sudah presensi hari ini.
Langkah 4 - Tambahkan Menu Presensi di Sidebar
Buka file web/WEB-INF/views/partials/sidebar.jsp.
Tambahkan menu Presensi hanya untuk role
pegawai.
<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>
Hasilnya:
- admin tidak melihat menu presensi,
-
pegawai melihat menu
Home,Profile, danPresensi.
Langkah 5 - Buat Halaman JSP Presensi
Buat file web/WEB-INF/views/presensi.jsp.
Halaman ini menampilkan:
- identitas pegawai,
- status presensi hari ini,
- tombol
Datang, - tombol
Pulang, - riwayat presensi terbaru.
Struktur utamanya seperti ini:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<div class="page-header">
<h2>Presensi Pegawai</h2>
</div>
<c:if test="${not empty param.message}">
<div class="${empty param.status ? 'info' : param.status}">
<c:out value="${param.message}"/>
</div>
</c:if>
<div class="card">
<h3>Informasi Pegawai</h3>
<p><strong>NIP:</strong> <c:out value="${pegawai.nip}"/></p>
<p><strong>Nama:</strong> <c:out value="${pegawai.namaPegawai}"/></p>
<p><strong>Bagian:</strong> <c:out value="${pegawai.bagianNama}"/></p>
</div>
<div class="card">
<h3>Presensi Hari Ini</h3>
<p><strong>Tanggal:</strong> <c:out value="${presensiHariIni.tanggal}" default="-"/></p>
<p><strong>Jam Masuk:</strong> <c:out value="${presensiHariIni.jamMasuk}" default="-"/></p>
<p><strong>Jam Pulang:</strong> <c:out value="${presensiHariIni.jamPulang}" default="-"/></p>
<form method="post" action="${pageContext.request.contextPath}/presensi">
<button type="submit" name="action" value="datang" class="btn btn-add">Datang</button>
<button type="submit" name="action" value="pulang" class="btn btn-secondary">Pulang</button>
</form>
</div>
Lanjutkan dengan tabel riwayat presensi:
<div class="card">
<h3>Riwayat Presensi Terbaru</h3>
<table class="data-table">
<thead>
<tr>
<th>Tanggal</th>
<th>Jam Masuk</th>
<th>Jam Pulang</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<c:forEach var="presensi" items="${presensiList}">
<tr>
<td><c:out value="${presensi.tanggal}"/></td>
<td><c:out value="${presensi.jamMasuk}" default="-"/></td>
<td><c:out value="${presensi.jamPulang}" default="-"/></td>
<td><c:out value="${presensi.statusPresensi}"/></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
Langkah 6 - Hubungkan Dashboard Pegawai dengan Menu Presensi
Agar alur fitur terasa rapi, dashboard pegawai perlu memberi jalan cepat ke halaman presensi.
Buka file web/WEB-INF/views/dashboard/pegawai.jsp.
Tambahkan atau pastikan ada tombol seperti ini:
<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>
Dengan ini, pegawai bisa:
- login,
- masuk dashboard,
- klik
Buka Presensi, - mencatat datang atau pulang.
Langkah 7 - Pastikan Role Pegawai Sudah Siap
Fitur presensi bergantung pada data user login.
Pastikan model User.java punya pengecekan role
berikut:
public boolean isPegawai() {
return "pegawai".equals(role);
}
Pastikan juga UserDAO.java membaca
id_pegawai, nip, dan
nama_pegawai saat login.
Tanpa relasi ini, servlet presensi tidak bisa mengetahui pegawai yang sedang login.
Pengujian
Lakukan pengujian berikut:
- Login sebagai user role
pegawai. - Pastikan menu
Presensimuncul di sidebar. - Buka menu
Presensi. - Klik tombol
Datang. - Pastikan
jam_masukterisi. - Klik tombol
Pulang. - Pastikan
jam_pulangterisi. - Buka dashboard pegawai, lalu cek ringkasan presensi hari ini.
Checklist hasil yang diharapkan:
- Menu Presensi muncul untuk pegawai
- Halaman presensi bisa dibuka
- Tombol Datang menyimpan jam masuk
- Tombol Pulang menyimpan jam pulang
- Riwayat presensi tampil
- Dashboard pegawai punya shortcut ke presensi
Troubleshooting
| Masalah | Penyebab | Solusi |
|---|---|---|
| Menu Presensi tidak muncul | Role login bukan pegawai |
Cek data role pada tabel users
|
| Halaman presensi redirect ke index |
Session user kosong atau id_pegawai tidak
terhubung
|
Pastikan login berhasil dan user terhubung ke data pegawai |
| Tombol Datang tidak menyimpan data |
Query insert gagal atau tabel presensi belum
siap
|
Cek struktur tabel dan koneksi database |
| Tombol Pulang gagal | Belum ada data datang hari ini |
Klik Datang dulu sebelum Pulang
|
| Riwayat presensi kosong | Belum ada data pada tabel presensi |
Lakukan presensi datang dan pulang lalu refresh halaman |
Tugas Praktikum
-
Tambahkan kolom
keteranganpada form atau logika presensi jika pegawai datang terlambat. -
Ubah status presensi sehingga bisa membedakan
Hadir,Terlambat, danIzin. -
Batasi jam datang agar setelah jam tertentu otomatis diberi
status
Terlambat. - Tambahkan validasi agar presensi hanya bisa dilakukan satu kali datang dan satu kali pulang per hari.
Kesimpulan
Pada pertemuan ini, fitur Presensi Pegawai dibangun
dengan menghubungkan menu sidebar, servlet, DAO, model, dan
halaman JSP.
Fitur ini menjadi pelengkap penting setelah CRUD pegawai,
manajemen user, dan dashboard pegawai selesai dibuat. Dengan alur
ini, user role pegawai tidak hanya melihat data,
tetapi juga mulai berinteraksi langsung dengan proses bisnis
aplikasi presensi.