TKK_E32231405/app/dashboard/manajemen-posyandu/PosyanduFormModal.tsx

269 lines
13 KiB
TypeScript

'use client'
import { useState, useEffect } from 'react'
import { X, Save, Building2, MapPin, Phone, User, Map as MapIcon, Loader2, Info } from 'lucide-react'
import { supabase } from '@/lib/supabase'
import { useRouter } from 'next/navigation'
interface PosyanduFormModalProps {
isOpen: boolean
onClose: () => void
selectedData?: any | null
}
export function PosyanduFormModal({ isOpen, onClose, selectedData }: PosyanduFormModalProps) {
const router = useRouter()
const [loading, setLoading] = useState(false)
const [formData, setFormData] = useState({
nama_posyandu: '',
alamat: '',
kontak: '',
nama_petugas: '', // From petugas_posyandu_lokal
link_google_maps: '',
latitude: '',
longitude: ''
})
useEffect(() => {
if (selectedData) {
setFormData({
nama_posyandu: selectedData.nama_posyandu || '',
alamat: selectedData.alamat || '',
kontak: selectedData.kontak || '',
nama_petugas: selectedData.petugas?.[0]?.nama_petugas || '',
link_google_maps: selectedData.link_google_maps || '',
latitude: selectedData.latitude?.toString() || '',
longitude: selectedData.longitude?.toString() || ''
})
} else {
setFormData({
nama_posyandu: '',
alamat: '',
kontak: '',
nama_petugas: '',
link_google_maps: '',
latitude: '',
longitude: ''
})
}
}, [selectedData])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
try {
const payload = {
nama_posyandu: formData.nama_posyandu,
alamat: formData.alamat,
kontak: formData.kontak,
link_google_maps: formData.link_google_maps,
latitude: formData.latitude ? parseFloat(formData.latitude) : null,
longitude: formData.longitude ? parseFloat(formData.longitude) : null,
}
let posyanduId = selectedData?.id
if (selectedData) {
// Update
const { error } = await supabase.from('detail_posyandu').update(payload).eq('id', selectedData.id)
if (error) throw error
} else {
// Insert
const { data, error } = await supabase.from('detail_posyandu').insert(payload).select().single()
if (error) throw error
posyanduId = data.id
}
// Sync Petugas (Simple logic: one main officer for this Posyandu)
if (formData.nama_petugas) {
const petugasPayload = {
posyandu_id: posyanduId,
nama_petugas: formData.nama_petugas,
jabatan: 'Koordinator'
}
if (selectedData?.petugas?.[0]?.id) {
await supabase.from('petugas_posyandu_lokal')
.update(petugasPayload)
.eq('id', selectedData.petugas[0].id)
} else {
await supabase.from('petugas_posyandu_lokal')
.insert(petugasPayload)
}
}
router.refresh()
onClose()
} catch (err: any) {
alert('Gagal menyimpan data: ' + err.message)
} finally {
setLoading(false)
}
}
if (!isOpen) return null
return (
<div className="fixed inset-0 z-[60] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm">
<div className="bg-white w-full max-w-xl rounded-2xl border-2 border-black shadow-[12px_12px_0px_0px_rgba(0,0,0,1)] flex flex-col max-h-[90vh]">
{/* Header */}
<div className="flex items-center justify-between p-6 border-b-2 border-black bg-purple-600 text-white">
<div className="flex items-center gap-3">
<div className="p-2 bg-white/20 rounded-lg">
<Building2 className="w-5 h-5 text-white" />
</div>
<div>
<h2 className="text-xl font-bold leading-none">
{selectedData ? 'Edit Data Posyandu' : 'Tambah Posyandu Baru'}
</h2>
<p className="text-[10px] text-purple-200 uppercase tracking-widest mt-1">OPERASIONAL POSYANDU</p>
</div>
</div>
<button onClick={onClose} className="p-2 hover:bg-white/10 rounded-full transition-colors">
<X className="w-5 h-5" />
</button>
</div>
{/* Form Body */}
<form onSubmit={handleSubmit} className="p-6 overflow-y-auto flex flex-col gap-6">
{/* Nama Posyandu */}
<div className="flex flex-col gap-2">
<label className="text-xs font-black uppercase tracking-widest text-gray-500 flex items-center gap-2">
<Building2 className="w-3.5 h-3.5" />
Nama Posyandu
</label>
<input
required
type="text"
placeholder="Contoh: Posyandu Melati 01"
value={formData.nama_posyandu}
onChange={e => setFormData({ ...formData, nama_posyandu: e.target.value })}
className="w-full px-4 py-3 bg-gray-50 border-2 border-gray-100 focus:border-black rounded-xl text-sm font-bold outline-none transition-all"
/>
</div>
{/* Petugas & Kontak Section */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="flex flex-col gap-2">
<label className="text-xs font-black uppercase tracking-widest text-gray-500 flex items-center gap-2">
<User className="w-3.5 h-3.5" />
Nama Petugas
</label>
<input
required
type="text"
placeholder="Nama Koordinator"
value={formData.nama_petugas}
onChange={e => setFormData({ ...formData, nama_petugas: e.target.value })}
className="w-full px-4 py-3 bg-gray-50 border-2 border-gray-100 focus:border-black rounded-xl text-sm font-bold outline-none transition-all"
/>
</div>
<div className="flex flex-col gap-2">
<label className="text-xs font-black uppercase tracking-widest text-gray-500 flex items-center gap-2">
<Phone className="w-3.5 h-3.5" />
Kontak / No HP
</label>
<input
type="text"
placeholder="0812xxxx"
value={formData.kontak}
onChange={e => setFormData({ ...formData, kontak: e.target.value })}
className="w-full px-4 py-3 bg-gray-50 border-2 border-gray-100 focus:border-black rounded-xl text-sm font-bold outline-none transition-all"
/>
</div>
</div>
{/* Alamat */}
<div className="flex flex-col gap-2">
<label className="text-xs font-black uppercase tracking-widest text-gray-500 flex items-center gap-2">
<MapPin className="w-3.5 h-3.5" />
Alamat Lengkap
</label>
<textarea
required
rows={3}
placeholder="Jl. Raya No. 123..."
value={formData.alamat}
onChange={e => setFormData({ ...formData, alamat: e.target.value })}
className="w-full px-4 py-3 bg-gray-50 border-2 border-gray-100 focus:border-black rounded-xl text-sm font-bold outline-none transition-all resize-none"
/>
</div>
{/* Google Maps Section */}
<div className="p-4 bg-blue-50/50 rounded-2xl border-2 border-blue-100 flex flex-col gap-4">
<div className="flex items-center gap-2 text-blue-700">
<MapIcon className="w-4 h-4" />
<span className="text-xs font-bold uppercase tracking-widest">Titik Lokasi Google Maps</span>
</div>
<div className="flex flex-col gap-2">
<label className="text-[10px] font-black uppercase text-blue-400 tracking-wider">Link Google Maps (Share Link)</label>
<input
type="url"
placeholder="https://maps.app.goo.gl/..."
value={formData.link_google_maps}
onChange={e => setFormData({ ...formData, link_google_maps: e.target.value })}
className="w-full px-4 py-2.5 bg-white border-2 border-blue-100 focus:border-blue-500 rounded-lg text-xs font-semibold outline-none transition-all"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="flex flex-col gap-1.5">
<label className="text-[10px] font-black uppercase text-blue-400 tracking-wider">Latitude</label>
<input
type="text"
placeholder="-6.12345"
value={formData.latitude}
onChange={e => setFormData({ ...formData, latitude: e.target.value })}
className="w-full px-4 py-2 bg-white border-2 border-blue-100 focus:border-blue-500 rounded-lg text-xs font-semibold outline-none transition-all"
/>
</div>
<div className="flex flex-col gap-1.5">
<label className="text-[10px] font-black uppercase text-blue-400 tracking-wider">Longitude</label>
<input
type="text"
placeholder="106.12345"
value={formData.longitude}
onChange={e => setFormData({ ...formData, longitude: e.target.value })}
className="w-full px-4 py-2 bg-white border-2 border-blue-100 focus:border-blue-500 rounded-lg text-xs font-semibold outline-none transition-all"
/>
</div>
</div>
<div className="flex items-start gap-2 text-[10px] text-blue-500 leading-relaxed italic">
<Info className="w-3 h-3 mt-0.5 flex-shrink-0" />
<span>Input koordinat (Latitude & Longitude) agar titik lokasi akurat di sistem peta.</span>
</div>
</div>
{/* Footer Actions */}
<div className="flex items-center gap-3 mt-2">
<button
type="button"
onClick={onClose}
className="flex-1 py-3 border-2 border-black rounded-xl font-bold text-sm hover:bg-gray-50 transition-all"
>
Batal
</button>
<button
type="submit"
disabled={loading}
className="flex-[2] py-3 bg-black text-white font-black text-sm rounded-xl hover:bg-gray-800 transition-all flex items-center justify-center gap-2 shadow-[4px_4px_0px_0px_rgba(147,51,234,0.5)]"
>
{loading ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Save className="w-4 h-4" />
)}
Simpan Data
</button>
</div>
</form>
</div>
</div>
)
}