TKK_E32231405/app/dashboard/kelola-jadwal/action-jadwal.ts

244 lines
8.5 KiB
TypeScript

'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: [] }
}
}