feat(kesehatan): admin page ringkasan-kesehatan + sidebar entry - bump ke 0.1.51

- New page src/app/admin/(dashboard)/kesehatan/ringkasan-kesehatan/page.tsx
  konsumsi ringkasanKesehatanState (load + submit) dengan 7 NumberInput
  (3 count + 4 pct).
- Tambah Kesehatan_8 "Ringkasan Kesehatan" di 3 instance sidebar
  (list_PageAdmin.tsx).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-04 15:20:39 +08:00
parent 9ef5773cc2
commit fc6846f7a1
4 changed files with 214 additions and 2 deletions

View File

@@ -49,7 +49,9 @@ prisma/migrations/20260504000000_add_statistik_pct_ringkasan_kesehatan/migration
src/app/api/[[...slugs]]/_lib/kesehatan/ringkasan-kesehatan/updt.ts src/app/api/[[...slugs]]/_lib/kesehatan/ringkasan-kesehatan/updt.ts
src/app/api/[[...slugs]]/_lib/kesehatan/ringkasan-kesehatan/index.ts src/app/api/[[...slugs]]/_lib/kesehatan/ringkasan-kesehatan/index.ts
src/app/admin/(dashboard)/_state/kesehatan/ringkasan-kesehatan/ringkasanKesehatan.ts [NEW] src/app/admin/(dashboard)/_state/kesehatan/ringkasan-kesehatan/ringkasanKesehatan.ts [NEW]
package.json (0.1.48 → 0.1.50) src/app/admin/(dashboard)/kesehatan/ringkasan-kesehatan/page.tsx [NEW]
src/app/admin/_com/list_PageAdmin.tsx (3 sidebar instances → tambah Kesehatan_8)
package.json (0.1.48 → 0.1.51)
MIND/PLAN/task-statistik-kesehatan-ringkasan.md [NEW] MIND/PLAN/task-statistik-kesehatan-ringkasan.md [NEW]
``` ```
@@ -57,3 +59,4 @@ MIND/PLAN/task-statistik-kesehatan-ringkasan.md [NEW]
- `ba2b90be` feat(kesehatan): tambah 4 field statistik pct ke RingkasanKesehatanDesa - bump ke 0.1.49 - `ba2b90be` feat(kesehatan): tambah 4 field statistik pct ke RingkasanKesehatanDesa - bump ke 0.1.49
- `68a2a639` feat(kesehatan): tambah state file ringkasanKesehatan + bump ke 0.1.50 - `68a2a639` feat(kesehatan): tambah state file ringkasanKesehatan + bump ke 0.1.50
- (next) feat(kesehatan): admin page ringkasan-kesehatan + sidebar entry - bump ke 0.1.51

View File

@@ -1,6 +1,6 @@
{ {
"name": "desa-darmasaba", "name": "desa-darmasaba",
"version": "0.1.50", "version": "0.1.51",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",

View File

@@ -0,0 +1,194 @@
'use client';
import colors from '@/con/colors';
import {
Box,
Button,
Divider,
Group,
Loader,
NumberInput,
Paper,
SimpleGrid,
Stack,
Text,
Title,
} from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
import ringkasanKesehatanState from '../../_state/kesehatan/ringkasan-kesehatan/ringkasanKesehatan';
export default function RingkasanKesehatanPage() {
const router = useRouter();
const state = useProxy(ringkasanKesehatanState);
useEffect(() => {
state.findUnique.load();
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const setField = (key: keyof typeof state.update.form, value: number) => {
state.update.form[key] = Number.isFinite(value) ? value : 0;
};
const handleSubmit = async () => {
const ok = await state.update.submit();
if (ok) router.refresh();
};
const handleReset = () => {
state.update.reset();
if (state.findUnique.data) {
const d = state.findUnique.data;
state.update.form = {
ibuHamilAkh: d.ibuHamilAkh,
balitaTerdaftar: d.balitaTerdaftar,
alertStunting: d.alertStunting,
imunisasiLengkapPct: d.imunisasiLengkapPct,
pemeriksaanRutinPct: d.pemeriksaanRutinPct,
giziBaikPct: d.giziBaikPct,
targetStuntingPct: d.targetStuntingPct,
};
}
toast.info('Form dikembalikan ke data awal');
};
const isLoading = state.findUnique.loading;
const isSubmitting = state.update.loading;
return (
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Group mb="md" gap="sm">
<Button variant="subtle" onClick={() => router.back()} radius="md">
<IconArrowBack size={20} stroke={2} />
</Button>
<Title order={3} c="black">Ringkasan Kesehatan Desa</Title>
</Group>
<Paper
withBorder
w={{ base: '100%', md: '80%' }}
p="lg"
radius="md"
shadow="xl"
bg="white"
>
{isLoading ? (
<Group justify="center" py="xl">
<Loader />
</Group>
) : (
<Stack gap="lg">
<Box>
<Text fw={600} mb="xs">KPI Utama</Text>
<SimpleGrid cols={{ base: 1, md: 3 }} spacing="md">
<NumberInput
label="Ibu Hamil Aktif"
description="Jumlah ibu hamil yang aktif tercatat"
min={0}
value={state.update.form.ibuHamilAkh}
onChange={(v) => setField('ibuHamilAkh', Number(v))}
radius="md"
/>
<NumberInput
label="Balita Terdaftar"
description="Total balita terdaftar di posyandu"
min={0}
value={state.update.form.balitaTerdaftar}
onChange={(v) => setField('balitaTerdaftar', Number(v))}
radius="md"
/>
<NumberInput
label="Alert Stunting"
description="Jumlah balita kategori alert stunting"
min={0}
value={state.update.form.alertStunting}
onChange={(v) => setField('alertStunting', Number(v))}
radius="md"
/>
</SimpleGrid>
</Box>
<Divider />
<Box>
<Text fw={600} mb="xs">Statistik Kesehatan (%)</Text>
<SimpleGrid cols={{ base: 1, md: 2 }} spacing="md">
<NumberInput
label="Imunisasi Lengkap"
description="Persentase balita imunisasi lengkap (0-100)"
min={0}
max={100}
suffix="%"
value={state.update.form.imunisasiLengkapPct}
onChange={(v) => setField('imunisasiLengkapPct', Number(v))}
radius="md"
/>
<NumberInput
label="Pemeriksaan Rutin"
description="Persentase warga pemeriksaan rutin (0-100)"
min={0}
max={100}
suffix="%"
value={state.update.form.pemeriksaanRutinPct}
onChange={(v) => setField('pemeriksaanRutinPct', Number(v))}
radius="md"
/>
<NumberInput
label="Gizi Baik"
description="Persentase balita dengan status gizi baik (0-100)"
min={0}
max={100}
suffix="%"
value={state.update.form.giziBaikPct}
onChange={(v) => setField('giziBaikPct', Number(v))}
radius="md"
/>
<NumberInput
label="Target Stunting"
description="Target penurunan stunting (0-100)"
min={0}
max={100}
suffix="%"
value={state.update.form.targetStuntingPct}
onChange={(v) => setField('targetStuntingPct', Number(v))}
radius="md"
/>
</SimpleGrid>
</Box>
<Group justify="right">
<Button
variant="outline"
color="gray"
radius="md"
size="md"
onClick={handleReset}
disabled={isSubmitting}
>
Batal
</Button>
<Button
onClick={handleSubmit}
radius="md"
size="md"
disabled={isSubmitting}
style={{
background: isSubmitting
? 'linear-gradient(135deg, #cccccc, #eeeeee)'
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
color: '#fff',
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
}}
>
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
</Button>
</Group>
</Stack>
)}
</Paper>
</Box>
);
}

View File

@@ -166,6 +166,11 @@ export const devBar = [
id: "Kesehatan_7", id: "Kesehatan_7",
name: "Info Wabah/Penyakit", name: "Info Wabah/Penyakit",
path: "/admin/kesehatan/info-wabah-penyakit" path: "/admin/kesehatan/info-wabah-penyakit"
},
{
id: "Kesehatan_8",
name: "Ringkasan Kesehatan",
path: "/admin/kesehatan/ringkasan-kesehatan"
} }
] ]
}, },
@@ -602,6 +607,11 @@ export const navBar = [
id: "Kesehatan_7", id: "Kesehatan_7",
name: "Info Wabah/Penyakit", name: "Info Wabah/Penyakit",
path: "/admin/kesehatan/info-wabah-penyakit" path: "/admin/kesehatan/info-wabah-penyakit"
},
{
id: "Kesehatan_8",
name: "Ringkasan Kesehatan",
path: "/admin/kesehatan/ringkasan-kesehatan"
} }
] ]
}, },
@@ -1272,6 +1282,11 @@ export const role2 = [
id: "Kesehatan_7", id: "Kesehatan_7",
name: "Info Wabah/Penyakit", name: "Info Wabah/Penyakit",
path: "/admin/kesehatan/info-wabah-penyakit" path: "/admin/kesehatan/info-wabah-penyakit"
},
{
id: "Kesehatan_8",
name: "Ringkasan Kesehatan",
path: "/admin/kesehatan/ringkasan-kesehatan"
} }
] ]
}, },