170 lines
7.3 KiB
TypeScript
170 lines
7.3 KiB
TypeScript
'use client'
|
|
|
|
import { useActionState, useEffect, useState } from 'react'
|
|
import { updateAkunBalita } from '@/app/actions'
|
|
import { User, MapPin, Phone, Baby, Calendar, Lock, AtSign, CheckCircle, XCircle, X } from 'lucide-react'
|
|
|
|
interface AkunBalita {
|
|
id: string
|
|
nama_orang_tua: string
|
|
alamat: string | null
|
|
no_whatsapp: string | null
|
|
nama_anak: string
|
|
tanggal_lahir: string | null
|
|
username: string
|
|
password: string
|
|
}
|
|
|
|
interface Props {
|
|
pengguna: AkunBalita
|
|
}
|
|
|
|
interface ToastProps {
|
|
message: string
|
|
type: 'success' | 'error'
|
|
onClose: () => void
|
|
}
|
|
|
|
function Toast({ message, type, onClose }: ToastProps) {
|
|
useEffect(() => {
|
|
const timer = setTimeout(onClose, 4000)
|
|
return () => clearTimeout(timer)
|
|
}, [onClose])
|
|
|
|
return (
|
|
<div
|
|
className={`fixed top-6 right-6 z-50 flex items-center gap-3 px-5 py-4 rounded-xl border-2 shadow-[4px_4px_0px_0px_rgba(0,0,0,1)]
|
|
${type === 'success'
|
|
? 'bg-green-50 border-green-500 text-green-800'
|
|
: 'bg-red-50 border-red-500 text-red-800'
|
|
}`}
|
|
style={{ animation: 'slideIn 0.35s cubic-bezier(0.34, 1.56, 0.64, 1)' }}
|
|
>
|
|
{type === 'success'
|
|
? <CheckCircle className="w-5 h-5 text-green-600 flex-shrink-0" />
|
|
: <XCircle className="w-5 h-5 text-red-600 flex-shrink-0" />
|
|
}
|
|
<span className="font-semibold text-sm">{message}</span>
|
|
<button onClick={onClose} className="ml-2 p-1 rounded-full hover:bg-black/10 transition-colors">
|
|
<X className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function EditPenggunaForm({ pengguna }: Props) {
|
|
const [state, formAction, isPending] = useActionState(updateAkunBalita, null)
|
|
const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null)
|
|
|
|
useEffect(() => {
|
|
if (state) {
|
|
setToast({ message: state.message, type: state.success ? 'success' : 'error' })
|
|
}
|
|
}, [state])
|
|
|
|
const inputClass = "border-2 border-gray-200 rounded-lg p-3 focus:outline-none focus:border-black transition-colors w-full text-sm"
|
|
const labelClass = "text-sm font-bold flex items-center gap-2 mb-1"
|
|
|
|
return (
|
|
<>
|
|
{toast && <Toast message={toast.message} type={toast.type} onClose={() => setToast(null)} />}
|
|
<style>{`
|
|
@keyframes slideIn {
|
|
from { transform: translateX(110%); opacity: 0; }
|
|
to { transform: translateX(0); opacity: 1; }
|
|
}
|
|
`}</style>
|
|
|
|
<form action={formAction} className="flex flex-col gap-5">
|
|
<input type="hidden" name="id" value={pengguna.id} />
|
|
|
|
{/* Section: Data Orang Tua */}
|
|
<div className="pb-2 mb-1 border-b border-gray-100">
|
|
<p className="text-xs font-bold uppercase tracking-widest text-gray-400">Data Orang Tua</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
{/* Nama Orang Tua */}
|
|
<div>
|
|
<label className={labelClass}><User className="w-4 h-4" /> Nama Orang Tua <span className="text-red-400">*</span></label>
|
|
<input type="text" name="nama_orang_tua" defaultValue={pengguna.nama_orang_tua} className={inputClass} required />
|
|
</div>
|
|
|
|
{/* No WhatsApp */}
|
|
<div>
|
|
<label className={labelClass}><Phone className="w-4 h-4" /> No. WhatsApp</label>
|
|
<input type="tel" name="no_whatsapp" defaultValue={pengguna.no_whatsapp || ''} className={inputClass} placeholder="08xxxxxxxxxx" />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Alamat */}
|
|
<div>
|
|
<label className={labelClass}><MapPin className="w-4 h-4" /> Alamat</label>
|
|
<textarea
|
|
name="alamat"
|
|
defaultValue={pengguna.alamat || ''}
|
|
className={`${inputClass} resize-none`}
|
|
rows={3}
|
|
placeholder="Masukkan alamat lengkap..."
|
|
/>
|
|
</div>
|
|
|
|
{/* Section: Data Anak */}
|
|
<div className="pb-2 mb-1 border-b border-gray-100 mt-2">
|
|
<p className="text-xs font-bold uppercase tracking-widest text-gray-400">Data Anak</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
{/* Nama Anak */}
|
|
<div>
|
|
<label className={labelClass}><Baby className="w-4 h-4" /> Nama Anak <span className="text-red-400">*</span></label>
|
|
<input type="text" name="nama_anak" defaultValue={pengguna.nama_anak} className={inputClass} required />
|
|
</div>
|
|
|
|
{/* Tanggal Lahir */}
|
|
<div>
|
|
<label className={labelClass}><Calendar className="w-4 h-4" /> Tanggal Lahir</label>
|
|
<input
|
|
type="date"
|
|
name="tanggal_lahir"
|
|
defaultValue={pengguna.tanggal_lahir || ''}
|
|
className={inputClass}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Section: Akun */}
|
|
<div className="pb-2 mb-1 border-b border-gray-100 mt-2">
|
|
<p className="text-xs font-bold uppercase tracking-widest text-gray-400">Data Akun</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
{/* Username */}
|
|
<div>
|
|
<label className={labelClass}><AtSign className="w-4 h-4" /> Username <span className="text-red-400">*</span></label>
|
|
<input type="text" name="username" defaultValue={pengguna.username} className={inputClass} required />
|
|
</div>
|
|
|
|
{/* Password */}
|
|
<div>
|
|
<label className={labelClass}><Lock className="w-4 h-4" /> Password <span className="text-red-400">*</span></label>
|
|
<input type="text" name="password" defaultValue={pengguna.password} className={inputClass} required />
|
|
<p className="text-[10px] text-gray-400 mt-1">Pastikan password aman dan mudah diingat.</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Submit */}
|
|
<div className="pt-4">
|
|
<button
|
|
type="submit"
|
|
disabled={isPending}
|
|
className="w-full bg-black text-white font-bold py-4 rounded-lg hover:bg-gray-800 transition-all shadow-[4px_4px_0px_0px_rgba(0,0,0,0.2)] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,0.2)] hover:translate-x-[2px] hover:translate-y-[2px] disabled:opacity-60 disabled:cursor-not-allowed disabled:transform-none"
|
|
>
|
|
{isPending ? 'Menyimpan...' : 'Simpan Perubahan'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</>
|
|
)
|
|
}
|