Membangun Kanban Board Rekrutmen Tanpa SPA: Pendekatan Laravel Blade & Alpine.js
Cara merancang fitur Applicant Tracking System (ATS) interaktif dengan drag-and-drop Kanban menggunakan Laravel Blade, SortableJS, dan Alpine.js.
Table of Contents
Saat merancang fitur Applicant Tracking System (ATS) dalam sistem HRIS, antarmuka Kanban Board visual adalah pilihan terbaik untuk melacak alur kandidat (dari tahap Applied, Screening, Interview, Offer, hingga Hired/Rejected). Tim rekrutmen dapat memindahkan kandidat antar tahap cukup dengan melakukan drag-and-drop.
Banyak developer langsung berasumsi bahwa untuk membuat fitur drag-and-drop yang interaktif seperti ini, kita harus menggunakan kerangka kerja Single Page Application (SPA) yang berat seperti React atau Vue di frontend.
Namun, di proyek ForgeLenta HRIS, kami membuktikan bahwa kita bisa membangun Kanban Board rekrutmen yang sangat interaktif dan responsif menggunakan Laravel Blade, Alpine.js, dan library ringan SortableJS.
Mengapa Menghindari SPA untuk Fitur Ini?
- Kompleksitas State: Memisahkan frontend (SPA) dan backend (API) memerlukan manajemen state (seperti Redux/Pinia) dan penanganan rute terpisah yang menambah beban pengembangan.
- Setup Lebih Lama: Memerlukan bundler tambahan, penataan ulang skema otentikasi (JWT/Sanctum), dan penanganan CORS.
- Penyajian Cepat: Laravel Blade dapat me-render struktur HTML awal secara instan dari database, sementara Alpine.js memberikan sentuhan reaktivitas mikro yang cukup untuk operasi drag-and-drop.
Arsitektur Kanban Board
Kanban Board terdiri dari beberapa kolom yang mewakili status kandidat. Setiap kolom berisi daftar kartu (card) kandidat. Saat kartu dipindahkan ke kolom lain, kita memicu request AJAX ke server backend untuk memperbarui status kandidat tersebut di database.
1. Struktur HTML Blade dan Alpine.js
Kami menggunakan modul directive Alpine.js untuk membungkus instansiasi SortableJS pada setiap kolom Kanban:
<div class="grid grid-cols-5 gap-4 overflow-x-auto p-4" x-data="kanbanBoard()">
<!-- Loop Tahap/Kolom -->
@foreach($stages as $stage)
<div class="bg-gray-50 dark:bg-gray-900 p-4 rounded-lg flex flex-col min-w-[280px]">
<h3 class="font-semibold text-gray-700 dark:text-gray-200 mb-3">{{ $stage->name }}</h3>
<!-- List Kartu Kandidat -->
<div
class="kanban-list flex-1 space-y-3 min-h-[300px]"
data-stage-id="{{ $stage->id }}"
x-init="initSortable($el)"
>
@foreach($stage->applicants as $applicant)
<div
class="bg-white dark:bg-gray-800 p-3 rounded shadow cursor-grab active:cursor-grabbing border-l-4 border-brand-500"
data-applicant-id="{{ $applicant->id }}"
>
<h4 class="font-medium text-gray-800 dark:text-white text-sm">{{ $applicant->name }}</h4>
<p class="text-xs text-gray-500 mt-1">{{ $applicant->applied_job }}</p>
</div>
@endforeach
</div>
</div>
@endforeach
</div>2. Logika Frontend Alpine.js
Alpine.js mendefinisikan fungsi pembungkus yang mendengarkan event seret dan kirim data ke server via axios:
document.addEventListener('alpine:init', () => {
Alpine.data('kanbanBoard', () => ({
initSortable(element) {
new Sortable(element, {
group: 'applicants', // Mengizinkan perpindahan antar kolom yang satu grup
animation: 150,
ghostClass: 'opacity-50',
async onEnd(evt) {
const applicantId = evt.item.getAttribute('data-applicant-id');
const toStageId = evt.to.getAttribute('data-stage-id');
try {
// Pemicu AJAX request untuk update database secara background
await axios.patch(`/admin/applicants/${applicantId}/stage`, {
stage_id: toStageId
});
console.log(`Applicant ${applicantId} moved to stage ${toStageId}`);
} catch (error) {
console.error("Gagal memindahkan kandidat:", error);
// Kembalikan posisi kartu jika request gagal
window.location.reload();
}
}
});
}
}));
});3. Controller Backend Laravel 12
Di sisi backend, controller bertugas memproses pembaruan status pelamar secara cepat dan mengembalikan respon sukses:
public function updateStage(Request $request, Applicant $applicant)
{
$request->validate([
'stage_id' => 'required|exists:recruitment_stages,id'
]);
// Update status di database
$applicant->update([
'recruitment_stage_id' => $request->stage_id
]);
// Opsional: Picu notifikasi otomatis ke kandidat jika dipindahkan ke tahap wawancara
if ($applicant->stage->name === 'Interview') {
$applicant->user->notify(new InterviewInvitationNotification($applicant));
}
return response()->json([
'status' => 'success',
'message' => 'Status kandidat berhasil diperbarui.'
]);
}Keuntungan Menggunakan Pendekatan Ini
- Performa Instan: Waktu pemuatan awal halaman sangat cepat karena HTML langsung dirender dari server.
- Kompak & Ringan: Tidak membutuhkan pustaka tambahan yang membengkak di browser, cukup Alpine.js (~8KB) dan SortableJS (~12KB).
- Membaca Data Langsung: Kita tetap bisa menggunakan relasi database ORM Laravel Eloquent yang elegan secara langsung tanpa konversi API JSON kompleks.
Kesimpulan
Pendekatan hibrida (Server-rendered HTML + Lightweight Client Script) seperti perpaduan Laravel Blade, Alpine.js, dan SortableJS sangat ideal untuk fitur operasional internal seperti Kanban Board di sistem HRIS. Ini adalah solusi seimbang yang meminimalkan kerumitan kode tanpa mengorbankan pengalaman pengguna yang modern dan dinamis.