'use server' import { supabase } from '@/lib/supabase' import { revalidatePath } from 'next/cache' export async function generateInstantSchedule(prevState: any, formData: FormData) { const tanggal = formData.get('tanggal') as string const startStr = (formData.get('jam_mulai') as string) || '08:00' const endStr = (formData.get('jam_selesai') as string) || '11:00' const editedBy = formData.get('edited_by') as string if (!tanggal) { return { success: false, message: 'Tanggal pelaksanaan harus dipilih.' } } // Lead time validation: Must be at least tomorrow const inputDate = new Date(tanggal) const tomorrow = new Date() tomorrow.setHours(0, 0, 0, 0) tomorrow.setDate(tomorrow.getDate() + 1) if (inputDate < tomorrow) { return { success: false, message: 'Penjadwalan harus dilakukan minimal 1 hari sebelumnya (Besok atau lusa).' } } try { // 1. Fetch all registered Posyandu const { data: allPosyandu, error: fetchErr } = await supabase .from('detail_posyandu') .select('id, nama_posyandu') if (fetchErr) throw fetchErr if (!allPosyandu || allPosyandu.length === 0) { return { success: false, message: 'Tidak ada data Posyandu terdaftar.' } } // 2. Shuffle Posyandu randomly const shuffled = [...allPosyandu].sort(() => Math.random() - 0.5) // 3. Prepare schedules // Calculate duration of the first session const [hStart, mStart] = startStr.split(':').map(Number) const [hEnd, mEnd] = endStr.split(':').map(Number) const durationMinutes = (hEnd * 60 + mEnd) - (hStart * 60 + mStart) if (durationMinutes <= 0) { return { success: false, message: 'Jam selesai harus setelah jam mulai.' } } const schedulePack = shuffled.map((posyandu, index) => { const dayOffset = Math.floor(index / 3) const positionInDay = index % 3 // Calculate date for this posyandu const dateObj = new Date(tanggal) dateObj.setDate(dateObj.getDate() + dayOffset) const currentTanggal = dateObj.toISOString().split('T')[0] // Calculate time for this posyandu let startTime = new Date(`1970-01-01T${startStr}:00`) if (positionInDay > 0) { // Add (duration + gap) for each previous session in the same day startTime.setMinutes(startTime.getMinutes() + positionInDay * (durationMinutes + 60)) } const sessionEnd = new Date(startTime) sessionEnd.setMinutes(sessionEnd.getMinutes() + durationMinutes) const pad = (n: number) => n.toString().padStart(2, '0') const jam_mulai = `${pad(startTime.getHours())}:${pad(startTime.getMinutes())}:00` const jam_selesai = `${pad(sessionEnd.getHours())}:${pad(sessionEnd.getMinutes())}:00` return { posyandu_id: posyandu.id, tanggal: currentTanggal, jam_mulai, jam_selesai, diedit_oleh: editedBy } }) // 4. Batch Insert const { error: insertErr } = await supabase .from('jadwal_posyandu') .insert(schedulePack) if (insertErr) throw insertErr revalidatePath('/dashboard/kelola-jadwal') return { success: true, message: `Berhasil menjadwalkan ${shuffled.length} Posyandu!` } } catch (error: any) { console.error('Scheduling error:', error) return { success: false, message: error.message || 'Terjadi kesalahan saat membuat jadwal.' } } } export async function deleteSchedulesByDate(date: string) { try { const { error } = await supabase .from('jadwal_posyandu') .delete() .eq('tanggal', date) if (error) throw error revalidatePath('/dashboard/kelola-jadwal') return { success: true, message: 'Jadwal hari tersebut telah dihapus.' } } catch (error: any) { return { success: false, message: error.message } } } export async function deleteJadwal(id: string) { try { const { error } = await supabase .from('jadwal_posyandu') .delete() .eq('id', id) if (error) throw error revalidatePath('/dashboard/kelola-jadwal') return { success: true, message: 'Jadwal berhasil dihapus.' } } catch (error: any) { return { success: false, message: error.message } } } export async function updateJadwal(prevState: any, formData: FormData) { const id = formData.get('id') as string const tanggal = formData.get('tanggal') as string const jam_mulai = formData.get('jam_mulai') as string const jam_selesai = formData.get('jam_selesai') as string const editedBy = formData.get('edited_by') as string if (!id || !tanggal || !jam_mulai || !jam_selesai) { return { success: false, message: 'Field wajib tidak boleh kosong.' } } try { const { error } = await supabase .from('jadwal_posyandu') .update({ tanggal, jam_mulai: jam_mulai.length === 5 ? `${jam_mulai}:00` : jam_mulai, jam_selesai: jam_selesai.length === 5 ? `${jam_selesai}:00` : jam_selesai, diedit_oleh: editedBy, updated_at: new Date().toISOString() }) .eq('id', id) if (error) throw error revalidatePath('/dashboard/kelola-jadwal') return { success: true, message: 'Jadwal berhasil diperbarui!' } } catch (error: any) { console.error('Update error:', error) return { success: false, message: error.message || 'Gagal memperbarui jadwal.' } } } export async function getOccupiedSlots(date: string) { try { const { data, error } = await supabase .from('jadwal_posyandu') .select('jam_mulai, jam_selesai, posyandu_id') .eq('tanggal', date) if (error) throw error return { success: true, slots: data.map(s => ({ start: s.jam_mulai.slice(0, 5), end: s.jam_selesai.slice(0, 5), posyandu_id: s.posyandu_id })) } } catch (error: any) { return { success: false, slots: [] } } } export async function archiveSchedulesByMonth(month: number, year: number) { try { const startOfMonth = `${year}-${String(month + 1).padStart(2, '0')}-01` const endOfMonth = new Date(year, month + 1, 0).toISOString().split('T')[0] const { data: targets, error: fetchErr } = await supabase .from('jadwal_posyandu') .select('id, diedit_oleh') .gte('tanggal', startOfMonth) .lte('tanggal', endOfMonth) .not('diedit_oleh', 'ilike', '[HISTORY]%') if (fetchErr) throw fetchErr if (!targets || targets.length === 0) return { success: true, message: 'Tidak ada jadwal aktif untuk diarsipkan.' } // Update each target to have [HISTORY] prefix for (const item of targets) { const { error: updErr } = await supabase .from('jadwal_posyandu') .update({ diedit_oleh: `[HISTORY] ${item.diedit_oleh}` }) .eq('id', item.id) if (updErr) throw updErr } revalidatePath('/dashboard/kelola-jadwal') return { success: true, message: 'Seluruh jadwal bulan ini telah dipindahkan ke Histori!' } } catch (error: any) { return { success: false, message: error.message || 'Gagal mengarsipkan jadwal.' } } } export async function deleteAllHistory() { try { const { error } = await supabase .from('jadwal_posyandu') .delete() .ilike('diedit_oleh', '[HISTORY]%') if (error) throw error revalidatePath('/dashboard/kelola-jadwal') return { success: true, message: 'Seluruh riwayat jadwal berhasil dihapus permanen!' } } catch (error: any) { return { success: false, message: error.message || 'Gagal menghapus histori.' } } } export async function getPetugasLokalByPosyandu(posyanduId: string) { try { const { data, error } = await supabase .from('petugas_posyandu_lokal') .select('nama_petugas, nomor_hp, jabatan') .eq('posyandu_id', posyanduId) if (error) throw error return { success: true, petugas: data } } catch (error: any) { return { success: false, petugas: [] } } }