198 lines
9.9 KiB
TypeScript
198 lines
9.9 KiB
TypeScript
import { cookies } from 'next/headers'
|
|
import { redirect } from 'next/navigation'
|
|
import { supabase } from '@/lib/supabase'
|
|
import { LogoutButton } from '@/components/logout-button'
|
|
import { ArrowLeft, User, MapPin, Phone, Baby, Calendar } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { Mars, Venus } from 'lucide-react'
|
|
import { HasilStuntingTable } from './HasilStuntingTable'
|
|
import { PerkembanganChart } from './PerkembanganChart'
|
|
|
|
interface Props {
|
|
params: Promise<{ id: string }>
|
|
}
|
|
|
|
function ReadField({
|
|
icon,
|
|
label,
|
|
value,
|
|
accent,
|
|
}: {
|
|
icon: React.ReactNode
|
|
label: string
|
|
value: string | null | undefined
|
|
accent?: 'blue' | 'pink'
|
|
}) {
|
|
return (
|
|
<div className="flex flex-col gap-1.5 p-4 rounded-xl border-2 border-gray-100 bg-gray-50/50 hover:border-gray-200 transition-colors">
|
|
<div className="flex items-center gap-2 text-[10px] font-bold uppercase tracking-widest text-gray-400">
|
|
<span className="text-gray-400">{icon}</span>
|
|
{label}
|
|
</div>
|
|
<p className={`text-base font-bold ${accent === 'blue' ? 'text-blue-600' : accent === 'pink' ? 'text-pink-600' : 'text-black'}`}>
|
|
{value || <span className="text-gray-300 font-normal italic">Tidak ada data</span>}
|
|
</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default async function DetailPenggunaKelolaPage({ params }: Props) {
|
|
const { id } = await params
|
|
|
|
const cookieStore = await cookies()
|
|
const sessionCookie = cookieStore.get('user_session')
|
|
if (!sessionCookie) redirect('/')
|
|
|
|
const session = JSON.parse(sessionCookie.value)
|
|
if (session.role !== 'admin') redirect('/dashboard')
|
|
|
|
const { data: pengguna, error } = await supabase
|
|
.from('akun_balita')
|
|
.select('id, nama_orang_tua, alamat, no_whatsapp, nama_anak, jenis_kelamin, tanggal_lahir')
|
|
.eq('id', id)
|
|
.single()
|
|
|
|
if (error || !pengguna) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center text-red-500 font-semibold">
|
|
Data tidak ditemukan.
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Fetch hasil pengukuran stunting milik balita ini
|
|
const { data: hasilData } = await supabase
|
|
.from('hasil_stunting_balita')
|
|
.select('id, tinggi_badan, berat_badan, status_stunting, pesan_ai, tanggal_upload, nama_posyandu')
|
|
.eq('id_balita', pengguna.id)
|
|
.order('tanggal_upload', { ascending: false })
|
|
|
|
const formatDate = (d: string | null) => {
|
|
if (!d) return null
|
|
return new Date(d).toLocaleDateString('id-ID', {
|
|
day: 'numeric', month: 'long', year: 'numeric'
|
|
})
|
|
}
|
|
|
|
const isLaki = pengguna.jenis_kelamin?.toLowerCase().includes('laki')
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white font-sans text-black flex flex-col">
|
|
{/* Header */}
|
|
<header className="flex justify-between items-center px-8 py-6 border-b border-gray-100">
|
|
<div className="flex items-center gap-4">
|
|
<Link href="/dashboard/kelola-data" className="group flex items-center gap-2 text-sm font-bold hover:text-gray-600 transition-colors">
|
|
<div className="p-2 rounded-full border border-black flex items-center justify-center group-hover:bg-black group-hover:text-white transition-all">
|
|
<ArrowLeft className="h-5 w-5" />
|
|
</div>
|
|
<span className="hidden md:block">Kembali ke Daftar</span>
|
|
</Link>
|
|
<div className="h-8 w-px bg-gray-200 mx-2 hidden md:block"></div>
|
|
<div className="flex flex-col justify-center">
|
|
<h1 className="text-xl font-bold leading-none">Detail Pengguna</h1>
|
|
<p className="text-[10px] text-gray-500 tracking-widest uppercase mt-0.5">REVIEW DATA</p>
|
|
</div>
|
|
</div>
|
|
<LogoutButton />
|
|
</header>
|
|
|
|
<main className="p-8 max-w-5xl mx-auto flex-1 w-full">
|
|
<div className="bg-white rounded-xl border border-black shadow-[8px_8px_0px_0px_rgba(0,0,0,1)] overflow-hidden">
|
|
|
|
{/* Card Hero */}
|
|
<div className="bg-black text-white px-8 py-6 flex items-center gap-5">
|
|
<div className="w-16 h-16 rounded-full bg-white/10 border-2 border-white/20 flex items-center justify-center text-2xl font-black flex-shrink-0">
|
|
{pengguna.nama_orang_tua?.charAt(0).toUpperCase() ?? '?'}
|
|
</div>
|
|
<div>
|
|
<h2 className="text-2xl font-black">{pengguna.nama_orang_tua}</h2>
|
|
<div className="flex items-center gap-2 mt-1.5">
|
|
<Baby className="w-3.5 h-3.5 text-gray-400" />
|
|
<span className="text-sm text-gray-300">{pengguna.nama_anak ?? '-'}</span>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="p-8 flex flex-col gap-8">
|
|
|
|
{/* Section: Data Orang Tua */}
|
|
<div>
|
|
<div className="flex items-center gap-2 mb-4">
|
|
<User className="w-4 h-4 text-gray-500" />
|
|
<p className="text-xs font-bold uppercase tracking-widest text-gray-500">Data Orang Tua</p>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
<div className="md:col-span-2">
|
|
<ReadField icon={<User className="w-3.5 h-3.5" />} label="Nama Ibu / Orang Tua" value={pengguna.nama_orang_tua} />
|
|
</div>
|
|
<div className="md:col-span-2">
|
|
<ReadField icon={<MapPin className="w-3.5 h-3.5" />} label="Alamat" value={pengguna.alamat} />
|
|
</div>
|
|
<div className="md:col-span-2">
|
|
<ReadField icon={<Phone className="w-3.5 h-3.5" />} label="No. WhatsApp" value={pengguna.no_whatsapp} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border-t-2 border-dashed border-gray-100" />
|
|
|
|
{/* Section: Data Anak */}
|
|
<div>
|
|
<div className="flex items-center gap-2 mb-4">
|
|
<Baby className="w-4 h-4 text-gray-500" />
|
|
<p className="text-xs font-bold uppercase tracking-widest text-gray-500">Data Anak</p>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
<div className="md:col-span-2">
|
|
<ReadField icon={<Baby className="w-3.5 h-3.5" />} label="Nama Anak" value={pengguna.nama_anak} />
|
|
</div>
|
|
<ReadField
|
|
icon={isLaki ? <Mars className="w-3.5 h-3.5" /> : <Venus className="w-3.5 h-3.5" />}
|
|
label="Jenis Kelamin"
|
|
value={pengguna.jenis_kelamin}
|
|
accent={isLaki ? 'blue' : 'pink'}
|
|
/>
|
|
<ReadField
|
|
icon={<Calendar className="w-3.5 h-3.5" />}
|
|
label="Tanggal Lahir"
|
|
value={formatDate(pengguna.tanggal_lahir)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Separator: Perkembangan */}
|
|
<div className="flex items-center gap-4 py-2">
|
|
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-gray-200 to-gray-200" />
|
|
<div className="flex items-center gap-2 px-4 py-1.5 bg-gray-50 border border-gray-200 rounded-full">
|
|
<div className="w-1.5 h-1.5 rounded-full bg-blue-300" />
|
|
<span className="text-[10px] font-bold uppercase tracking-widest text-gray-400">Grafik Perkembangan</span>
|
|
<div className="w-1.5 h-1.5 rounded-full bg-emerald-300" />
|
|
</div>
|
|
<div className="flex-1 h-px bg-gradient-to-l from-transparent via-gray-200 to-gray-200" />
|
|
</div>
|
|
|
|
{/* Chart Perkembangan Tinggi & Berat */}
|
|
<PerkembanganChart data={hasilData ?? []} />
|
|
|
|
{/* Professional Separator */}
|
|
<div className="flex items-center gap-4 py-2">
|
|
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-gray-200 to-gray-200" />
|
|
<div className="flex items-center gap-2 px-4 py-1.5 bg-gray-50 border border-gray-200 rounded-full">
|
|
<div className="w-1.5 h-1.5 rounded-full bg-gray-400" />
|
|
<span className="text-[10px] font-bold uppercase tracking-widest text-gray-400">Riwayat Pengukuran</span>
|
|
<div className="w-1.5 h-1.5 rounded-full bg-gray-400" />
|
|
</div>
|
|
<div className="flex-1 h-px bg-gradient-to-l from-transparent via-gray-200 to-gray-200" />
|
|
</div>
|
|
|
|
{/* Tabel Riwayat Hasil Stunting */}
|
|
<HasilStuntingTable data={hasilData ?? []} pengguna={pengguna} />
|
|
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|