137 lines
6.6 KiB
TypeScript
137 lines
6.6 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useActionState, useEffect, useRef } from 'react'
|
|
import { X, Calendar, Clock, Loader2, Sparkles, AlertCircle } from 'lucide-react'
|
|
import { generateInstantSchedule } from './action-jadwal'
|
|
import { showSwal } from '@/lib/swal'
|
|
|
|
interface Props {
|
|
isOpen: boolean
|
|
onClose: () => void
|
|
adminName: string
|
|
}
|
|
|
|
export function InstantScheduleModal({ isOpen, onClose, adminName }: Props) {
|
|
const [state, formAction, isPending] = useActionState(generateInstantSchedule, null)
|
|
const processedStateRef = useRef<any>(null)
|
|
|
|
useEffect(() => {
|
|
// Detect NEW result from action
|
|
if (state && state !== processedStateRef.current) {
|
|
// 1. Immediately close the modal so it doesn't block the alert or persist
|
|
onClose()
|
|
|
|
// 2. Show the alert after closing the modal
|
|
if (state.success) {
|
|
showSwal.success('Berhasil!', `${state.message} Silakan gunakan tombol "Cetak Semua Jadwal" untuk mengunduh seluruh laporan PDF.`)
|
|
} else {
|
|
showSwal.error('Gagal!', state.message)
|
|
}
|
|
|
|
// 3. Mark this specific state object as processed
|
|
processedStateRef.current = state
|
|
}
|
|
}, [state, onClose])
|
|
|
|
// Reset processed state when modal is opened for a fresh experience
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
processedStateRef.current = null
|
|
}
|
|
}, [isOpen])
|
|
|
|
if (!isOpen) return null
|
|
|
|
const tomorrow = new Date()
|
|
tomorrow.setDate(tomorrow.getDate() + 1)
|
|
const minDate = tomorrow.toISOString().split('T')[0]
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm">
|
|
<div className="bg-white w-full max-w-md rounded-2xl border-2 border-black shadow-[6px_6px_0px_0px_rgba(0,0,0,1)] overflow-hidden animate-in zoom-in duration-200">
|
|
|
|
<div className="p-5 bg-red-500 border-b-2 border-black flex justify-between items-center text-white">
|
|
<div className="flex items-center gap-3">
|
|
<div className="p-1.5 bg-white/20 rounded-lg backdrop-blur-sm">
|
|
<Sparkles className="w-4 h-4 text-white" />
|
|
</div>
|
|
<h3 className="text-base font-black uppercase tracking-tight">Penjadwalan Instan</h3>
|
|
</div>
|
|
<button onClick={onClose} className="p-1.5 hover:bg-white/10 rounded-full transition-all">
|
|
<X className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<form action={formAction} className="p-6 flex flex-col gap-5">
|
|
<input type="hidden" name="edited_by" value={adminName} />
|
|
<input type="hidden" name="jam_mulai" value="08:00" />
|
|
<input type="hidden" name="jam_selesai" value="11:00" />
|
|
|
|
<div className="p-3 bg-blue-50 border border-blue-200 rounded-xl flex gap-x-2">
|
|
<AlertCircle className="w-4 h-4 text-blue-600 flex-shrink-0" />
|
|
<div className="flex flex-col gap-1">
|
|
<p className="text-[10px] text-blue-800 leading-relaxed font-bold uppercase tracking-tight">
|
|
Penjadwalan harus dilakukan minimal 1 hari sebelumnya.
|
|
</p>
|
|
<p className="text-[10px] text-blue-800 leading-relaxed font-bold uppercase tracking-tight">
|
|
Sesi tetap: 08:00 - 11:00 WIB (Selingan 1 Jam).
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tanggal */}
|
|
<div className="flex flex-col gap-2">
|
|
<label className="text-[9px] font-black uppercase tracking-widest text-gray-400 flex items-center gap-2">
|
|
<Calendar className="w-3.5 h-3.5" /> Pilih Tanggal Pelaksanaan
|
|
</label>
|
|
<input
|
|
type="date"
|
|
name="tanggal"
|
|
required
|
|
min={minDate}
|
|
defaultValue={minDate}
|
|
className="w-full p-3 border-2 border-black rounded-xl font-bold focus:outline-none focus:ring-4 focus:ring-red-500/10 transition-all text-sm"
|
|
/>
|
|
</div>
|
|
|
|
<div className="p-4 bg-gray-50 border-2 border-dashed border-gray-200 rounded-xl">
|
|
<label className="text-[9px] font-black uppercase tracking-widest text-gray-400 flex items-center gap-2 mb-2">
|
|
<Clock className="w-3.5 h-3.5" /> Waktu Sesi (Otomatis)
|
|
</label>
|
|
<div className="flex items-center gap-3">
|
|
<div className="px-3 py-1.5 bg-black text-white text-sm font-black rounded-lg">
|
|
08:00
|
|
</div>
|
|
<div className="h-0.5 w-4 bg-gray-300"></div>
|
|
<div className="px-3 py-1.5 bg-black text-white text-sm font-black rounded-lg">
|
|
11:00
|
|
</div>
|
|
<span className="text-[10px] font-black text-gray-400 uppercase tracking-widest ml-auto">Fixed Slot</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="pt-2">
|
|
<button
|
|
type="submit"
|
|
disabled={isPending}
|
|
className="w-full py-4 bg-black text-white font-black text-xs uppercase tracking-widest rounded-xl hover:bg-gray-800 transition-all shadow-[4px_4px_0px_0px_rgba(0,0,0,0.3)] hover:shadow-none hover:translate-x-[2px] hover:translate-y-[2px] flex items-center justify-center gap-2 disabled:opacity-50"
|
|
>
|
|
{isPending ? (
|
|
<>
|
|
<Loader2 className="w-5 h-5 animate-spin" />
|
|
Menghitung...
|
|
</>
|
|
) : (
|
|
<>
|
|
<Sparkles className="w-5 h-5" />
|
|
Buat Jadwal Sekarang
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|