Fix UI User Menu PPID & Kesehatan

This commit is contained in:
2025-08-27 15:39:13 +08:00
parent f15ef5a275
commit f9530c32eb
35 changed files with 3658 additions and 2084 deletions

View File

@@ -2,158 +2,155 @@
import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Box, Button, Center, Divider, Group, Image, List, ListItem, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Divider, Group, List, ListItem, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconAlertCircle, IconCalendar, IconInfoCircle } from '@tabler/icons-react';
import { useParams } from 'next/navigation';
import { useProxy } from 'valtio/utils';
function Page() {
const state = useProxy(artikelKesehatanState)
const params = useParams()
const state = useProxy(artikelKesehatanState);
const params = useParams();
useShallowEffect(() => {
state.findUnique.load(params?.id as string)
}, [])
state.findUnique.load(params?.id as string);
}, []);
if (!state.findUnique.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
<Stack py="xl" px="md">
<Skeleton h={420} radius="lg" />
<Skeleton h={20} mt="md" w="60%" />
<Skeleton h={20} w="40%" />
<Skeleton h={200} radius="md" />
</Stack>
)
);
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Stack pos="relative" bg={colors.Bg} py="xl" gap="xl">
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'}>
<Paper radius={10}>
<Box style={{ borderTopLeftRadius: 10, borderTopRightRadius: 10 }} bg={colors['blue-button']}>
<Text p={'md'} fz={{ base: "h3", md: "h2" }} c={colors['white-1']} fw={"bold"}>
Detail Lengkap Fasilitas Kesehatan
<Box px={{ base: 'md', md: 100 }}>
<Stack gap="lg">
<Paper radius="xl" shadow="md" withBorder>
<Box style={{ borderTopLeftRadius: 16, borderTopRightRadius: 16 }} bg={colors['blue-button']}>
<Text p="md" fz={{ base: 'h3', md: 'h2' }} c={colors['white-1']} fw="bold">
{state.findUnique.data.title || 'Detail Artikel Kesehatan'}
</Text>
</Box>
<Box p={'md'} >
<Stack gap={'xs'}>
<Center bg={'#DEE3E3FF'}>
<Image
w={'100%'}
src={'/api/img/dbd.png'}
alt='' />
</Center>
<Box>
<Text c={'#9A9D9DFF'} fz={{ base: 'h6', md: 'h5' }}>
{new Date(state.findUnique.data.createdAt).toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' })}
</Text>
</Box>
{/* Pendahuluan */}
<Text fz={'h4'} fw={"bold"}>
Pendahuluan
</Text>
<Divider />
<Text pb={10} fz={'h4'} ta={'justify'}>
{state.findUnique.data.introduction?.content}
</Text>
{/* Kenali Gejala DBD */}
<Text fz={'h4'} fw={"bold"}>
Kenali Gejala DBD
</Text>
<Divider />
<Text fz={'h4'}>
{state.findUnique.data.symptom?.title}
</Text>
<Text fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.symptom?.content }} />
{/* Cara Mencegah DBD */}
<Text fz={'h4'} fw={"bold"}>
{state.findUnique.data.prevention?.title}
</Text>
<Divider />
<Text fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.prevention?.content }} />
{/* Pertolongan Pertama Pada Penderita DBD */}
<Text fz={'h4'} fw={"bold"}>
{state.findUnique.data.firstaid?.title}
</Text>
<Divider />
<Text fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.firstaid?.content }} />
{/* Mitos dan Fakta tentang DBD */}
<Text fz={'h4'} fw={"bold"}>
{state.findUnique.data.mythvsfact?.title}
</Text>
<Divider />
<Box pb={10}>
<Table highlightOnHover withTableBorder withColumnBorders>
<TableThead>
<TableTr>
<TableTh fz={'h4'}>Mitos</TableTh>
<TableTh fz={'h4'}>Fakta</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{state.findUnique.data?.mythvsfact ? (
<TableTr>
<TableTd>
<Text fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.mythvsfact.mitos }} />
</TableTd>
<TableTd>
<Text fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.mythvsfact.fakta }} />
</TableTd>
</TableTr>
) : (
<TableTr>
<TableTd colSpan={3} style={{ textAlign: 'center' }}>Tidak ada data mitos dan fakta</TableTd>
</TableTr>
)}
</TableTbody>
</Table>
</Box>
{/* Kapan Harus ke Dokter */}
<Text fz={'h4'} fw={"bold"}>
Kapan Harus ke Dokter?
</Text>
<Divider />
<Text fz={'h4'}>
Segera bawa penderita ke fasilitas kesehatan jika mengalami:
</Text>
<Text fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.doctorsign.content }} />
{/* Kasus DBD di Wilayah Abiansemal */}
<Text fz={'h4'} fw={"bold"}>
Kasus DBD di Wilayah Abiansemal
</Text>
<Divider />
<Paper p={'lg'} bg={colors['blue-button-trans']}>
<Text fz={'h3'} c={colors['white-1']} fw={'bold'}>Informasi Lebih Lanjut</Text>
<Text fz={'h4'} c={colors['white-1']} fw={"bold"}>
Hotline DBD : <Text span fz={'h4'}>(0361) 123456</Text>
<Box p="lg">
<Stack gap="lg">
<Group gap="xs">
<IconCalendar size={18} color={colors['blue-button']} />
<Text c="dimmed" fz="sm">
{new Date(state.findUnique.data.createdAt).toLocaleDateString('id-ID', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</Text>
<Text fz={'h4'} c={colors['white-1']} fw={"bold"}>
WhatsApp Center : <Text span fz={'h4'}>081234567890</Text>
</Text>
<Text fz={'h4'} c={colors['white-1']} fw={"bold"}>
Email : <Text span fz={'h4'}>
p2p@dinkes.badungkab.go.id</Text>
</Text>
</Paper>
{/* Referensi */}
<Text fz={'h4'} fw={"bold"}>
Referensi
</Text>
<Divider />
<List pb={10} type='ordered'>
<ListItem fz={'h4'}>Kementerian Kesehatan RI. (2024). Pedoman Pencegahan dan Pengendalian DBD.</ListItem>
<ListItem fz={'h4'}>World Health Organization. (2024). Dengue Guidelines for Diagnosis, Treatment, Prevention and Control.</ListItem>
<ListItem fz={'h4'}>Dinas Kesehatan Kabupaten Badung. (2025). Laporan Surveilans DBD Triwulan I 2025.</ListItem>
</List>
<Group>
<Button fz={'lg'} bg={colors['blue-button']}>
Download Infografis Pencegahan DBD (PDF)
</Button>
<Button fz={'lg'} bg={'green'}>
Bagikan Artikel Ini
</Button>
</Group>
<Stack gap="lg">
<Box>
<Text fz="h4" fw="bold">Pendahuluan</Text>
<Divider my="xs" />
<Text fz="md" lh={1.6} ta="justify">
{state.findUnique.data.introduction?.content}
</Text>
</Box>
<Box>
<Text fz="h4" fw="bold">Kenali Gejala DBD</Text>
<Divider my="xs" />
<Text fz="md" fw="semibold">{state.findUnique.data.symptom?.title}</Text>
<Text fz="md" lh={1.6} ta="justify" dangerouslySetInnerHTML={{ __html: state.findUnique.data.symptom?.content }} />
</Box>
<Box>
<Text fz="h4" fw="bold">{state.findUnique.data.prevention?.title}</Text>
<Divider my="xs" />
<Text fz="md" lh={1.6} ta="justify" dangerouslySetInnerHTML={{ __html: state.findUnique.data.prevention?.content }} />
</Box>
<Box>
<Text fz="h4" fw="bold">{state.findUnique.data.firstaid?.title}</Text>
<Divider my="xs" />
<Text fz="md" lh={1.6} ta="justify" dangerouslySetInnerHTML={{ __html: state.findUnique.data.firstaid?.content }} />
</Box>
<Box>
<Text fz="h4" fw="bold">{state.findUnique.data.mythvsfact?.title}</Text>
<Divider my="xs" />
<Box pb="md">
<Table highlightOnHover withTableBorder withColumnBorders striped>
<TableThead>
<TableTr>
<TableTh fz="sm" fw="bold">Mitos</TableTh>
<TableTh fz="sm" fw="bold">Fakta</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{state.findUnique.data?.mythvsfact ? (
<TableTr>
<TableTd>
<Text fz="sm" lh={1.6} dangerouslySetInnerHTML={{ __html: state.findUnique.data.mythvsfact.mitos }} />
</TableTd>
<TableTd>
<Text fz="sm" lh={1.6} dangerouslySetInnerHTML={{ __html: state.findUnique.data.mythvsfact.fakta }} />
</TableTd>
</TableTr>
) : (
<TableTr>
<TableTd colSpan={2} ta="center" c="dimmed">Belum ada data mitos dan fakta</TableTd>
</TableTr>
)}
</TableTbody>
</Table>
</Box>
</Box>
<Box>
<Text fz="h4" fw="bold">Kapan Harus ke Dokter?</Text>
<Divider my="xs" />
<Group gap="xs" mb="xs">
<IconAlertCircle size={18} color="red" />
<Text fz="md">Segera bawa penderita ke fasilitas kesehatan jika mengalami:</Text>
</Group>
<Text fz="md" lh={1.6} dangerouslySetInnerHTML={{ __html: state.findUnique.data.doctorsign.content }} />
</Box>
<Box>
<Text fz="h4" fw="bold">Kasus DBD di Wilayah Abiansemal</Text>
<Divider my="xs" />
<Paper p="lg" radius="md" bg={colors['blue-button-trans']} withBorder>
<Group gap="xs" mb="sm">
<IconInfoCircle size={20} color={colors['white-1']} />
<Text fz="h4" c={colors['white-1']} fw="bold">Informasi Lebih Lanjut</Text>
</Group>
<Stack gap={4}>
<Text fz="sm" c={colors['white-1']}>Hotline DBD: <b>(0361) 123456</b></Text>
<Text fz="sm" c={colors['white-1']}>WhatsApp Center: <b>081234567890</b></Text>
<Text fz="sm" c={colors['white-1']}>Email: <b>p2p@dinkes.badungkab.go.id</b></Text>
</Stack>
</Paper>
</Box>
<Box>
<Text fz="h4" fw="bold">Referensi</Text>
<Divider my="xs" />
<List spacing="xs" size="sm" type="ordered">
<ListItem>Kementerian Kesehatan RI. (2024). Pedoman Pencegahan dan Pengendalian DBD.</ListItem>
<ListItem>World Health Organization. (2024). Dengue Guidelines for Diagnosis, Treatment, Prevention and Control.</ListItem>
<ListItem>Dinas Kesehatan Kabupaten Badung. (2025). Laporan Surveilans DBD Triwulan I 2025.</ListItem>
</List>
</Box>
</Stack>
</Stack>
</Box>
</Paper>

View File

@@ -1,8 +1,9 @@
'use client'
import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import colors from '@/con/colors';
import { Anchor, Box, Divider, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { Anchor, Box, Card, Divider, Group, Image, Loader, Paper, Stack, Text, Title, Tooltip } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconCalendar, IconChevronRight } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
@@ -16,36 +17,71 @@ function ArtikelKesehatanPage() {
if(!state.findMany.data){
return (
<Box py={10}>
<Skeleton h={500}/>
<Box py="xl" ta="center">
<Loader size="lg" color={colors['blue-button']} />
<Text mt="md" c="dimmed" fz="md">Memuat artikel kesehatan...</Text>
</Box>
)
}
if(state.findMany.data.length === 0){
return (
<Box py="xl" ta="center">
<Image src="/empty-state.svg" alt="Tidak ada data" w={220} mx="auto" />
<Text mt="md" fw="bold" fz="lg">Belum ada artikel kesehatan</Text>
<Text c="dimmed" fz="sm">Artikel akan tampil di sini setelah tersedia.</Text>
</Box>
)
}
return (
<Box>
<Paper p={'xl'} h={'112vh'} bg={colors['white-trans-1']}>
<Stack gap={'xs'}>
<Text ta={'center'} fw={"bold"} fz={'h3'} c={colors['blue-button']}>Artikel Kesehatan</Text>
{state.findMany.data.map((item) => (
<Box key={item.id}>
<Image pt={5} src={'/api/img/dbd.png'} alt="" />
<Text fz={'h4'} fw={'bold'} >
{item.title}
</Text>
<Text fz={'h6'} pb={10}>
Diposting: {new Date(item.createdAt).toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' })} | Dinas Kesehatan
</Text>
<Text fz={'h4'} pb={10} lineClamp={2}>
{item.content}
</Text>
<Anchor c={'black'} onClick={()=> router.push(`/darmasaba/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/${item.id}`)} variant='transparent'>
<Text c={colors['blue-button']} fz={'h4'} >
Baca Selengkapnya {'>>'}
</Text>
</Anchor>
</Box>
))}
<Divider color={colors['blue-button']} px={'xl'} />
<Paper p="xl" bg={colors['white-trans-1']} radius="xl" shadow="md">
<Stack gap="xl">
<Title order={2} ta="center" c={colors['blue-button']}>Artikel Kesehatan</Title>
<Divider color={colors['blue-button']} />
<Stack gap="lg">
{state.findMany.data.map((item) => (
<Card
key={item.id}
withBorder
radius="lg"
shadow="sm"
p="lg"
style={{ transition: 'transform 200ms ease' }}
onMouseEnter={(e) => (e.currentTarget.style.transform = 'translateY(-4px)')}
onMouseLeave={(e) => (e.currentTarget.style.transform = 'translateY(0)')}
>
<Card.Section>
<Image src={'/api/img/dbd.png'} alt={item.title} height={200} fit="cover" />
</Card.Section>
<Stack gap="xs" mt="md">
<Text fw="bold" fz="xl" c="dark">{item.title}</Text>
<Group gap="xs">
<IconCalendar size={16} color={colors['blue-button']} />
<Text fz="sm" c="dimmed">
{new Date(item.createdAt).toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' })} Dinas Kesehatan
</Text>
</Group>
<Text fz="md" c="dark" lineClamp={3}>
{item.content}
</Text>
<Tooltip label="Baca artikel lengkap">
<Anchor
onClick={()=> router.push(`/darmasaba/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/${item.id}`)}
variant="light"
c={colors['blue-button']}
>
<Group gap="xs">
<Text fw="bold" fz="md">Baca Selengkapnya</Text>
<IconChevronRight size={18} />
</Group>
</Anchor>
</Tooltip>
</Stack>
</Card>
))}
</Stack>
</Stack>
</Paper>
</Box>

View File

@@ -1,160 +1,353 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Box, Divider, List, ListItem, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { ActionIcon, Anchor, AspectRatio, Badge, Box, Button, Card, Chip, CopyButton, Divider, Grid, Group, List, ListItem, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, ThemeIcon, Title, Tooltip } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowRight, IconBrandWhatsapp, IconCheck, IconCopy, IconDeviceLandlinePhone, IconFileDownload, IconHeart, IconInfoCircle, IconMail, IconMapPin, IconMoodEmpty, IconSearch, IconStethoscope, IconUser, IconUsersGroup, IconWallet } from '@tabler/icons-react';
import { useParams } from 'next/navigation';
import { useMemo } from 'react';
import { useProxy } from 'valtio/utils';
interface Kontak {
telepon: string;
whatsapp: string;
email: string;
}
interface LayananItem {
nama: string;
keterangan?: string;
}
interface LayananUnggulan {
items: LayananItem[];
}
interface Lokasi {
mapsEmbed: string;
}
interface TarifDanLayanan {
layanan: string;
tarif: string | number;
gratisBpjs?: boolean;
}
function Page() {
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan)
const params = useParams()
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
const params = useParams();
useShallowEffect(() => {
state.findUnique.load(params?.id as string)
}, [])
state.findUnique.load(params?.id as string);
}, []);
if (!state.findUnique.data) {
const data = state.findUnique.data as any; // Temporary any to fix type issues
const nama = data?.name || 'Fasilitas Kesehatan';
const alamat = data?.informasiumum?.alamat || '-';
const jam = data?.informasiumum?.jamOperasional || '-';
const layananUnggulan = (data?.layananunggulan as LayananUnggulan)?.items || [];
const tenaga = data?.dokterdantenagamedis || null;
const fasilitasPendukungHtml = data?.fasilitaspendukung?.content || '';
const tarif = (data?.tarifdanlayanan as TarifDanLayanan) || null;
const kontak = (data?.kontak as Kontak) || {
telepon: '(0361) 123456',
whatsapp: '081234567890',
email: 'info@fasilitas-kesehatan.id'
};
const lokasi = (data?.lokasi as Lokasi) || {
mapsEmbed: 'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3945.272172359321!2d115.21836257533302!3d-8.569807186941553!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2dd23e9d99b9395f%3A0xb002795fdcb33b30!2sUPTD%20Puskesmas%20Abiansemal%20III!5e0!3m2!1sid!2sid!4v1744792682341!5m2!1sid!2sid'
};
const gratisBpjs = (data?.tarifdanlayanan as TarifDanLayanan)?.gratisBpjs ?? true;
const formatRupiah = useMemo(
() => (v?: number | string) => {
if (v === null || v === undefined || v === '') return '-';
const n = typeof v === 'string' ? Number(v) : v;
if (Number.isNaN(n as number)) return String(v);
return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(n as number);
},
[]
);
if (state.findUnique.loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
<Stack bg={colors.Bg} mih="100vh" p="lg" gap="lg">
<Box px={{ base: 'md', md: 100 }}>
<Skeleton h={32} w={220} />
</Box>
<Box px={{ base: 'md', md: 100 }}>
<Skeleton h={64} radius="lg" />
</Box>
<Box px={{ base: 'md', md: 100 }}>
<Grid gutter="lg">
<Grid.Col span={{ base: 12, md: 8 }}>
<Skeleton h={320} radius="lg" />
</Grid.Col>
<Grid.Col span={{ base: 12, md: 4 }}>
<Skeleton h={320} radius="lg" />
</Grid.Col>
</Grid>
</Box>
<Box px={{ base: 'md', md: 100 }}>
<Skeleton h={420} radius="lg" />
</Box>
</Stack>
)
);
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Stack bg={colors.Bg} py="xl" gap="xl">
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
<Group justify="space-between">
<Group gap="xs">
<BackButton />
</Group>
<Group gap="xs">
<Badge variant="gradient" gradient={{ from: 'blue', to: 'cyan' }} radius="xl" size="lg" leftSection={<IconHeart size={16} />}>Pelayanan Aktif</Badge>
<Badge variant="light" radius="xl" size="lg" leftSection={<IconInfoCircle size={16} />}>Jam: {jam}</Badge>
</Group>
</Group>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'}>
<Paper radius={10}>
<Box style={{ borderTopLeftRadius: 10, borderTopRightRadius: 10 }} bg={colors['blue-button']}>
<Text p={'md'} fz={{ base: "h3", md: "h2" }} c={colors['white-1']} fw={"bold"}>
Detail Lengkap Fasilitas Kesehatan
</Text>
</Box>
<Box p={'md'} >
<Stack gap={'xs'}>
{/* Informasi Umum */}
<Text fz={'h4'} fw={"bold"}>
Informasi Umum
</Text>
<Divider />
<Text fz={'h4'} fw={"bold"}>
Nama Fasilitas : <Text span fz={'h4'}>{state.findUnique.data?.name}</Text>
</Text>
<Text fz={'h4'} fw={"bold"}>
Alamat : <Text span fz={'h4'}>{state.findUnique.data?.informasiumum.alamat}</Text>
</Text>
<Text pb={10} fz={'h4'} fw={"bold"}>
Jam Operasional: : <Text span fz={'h4'}>{state.findUnique.data?.informasiumum.jamOperasional}</Text>
</Text>
{/* Layanan Unggulan */}
<Text fz={'h4'} fw={"bold"}>
Layanan Unggulan
</Text>
<Divider />
{/* Tenaga Medis */}
<Text fz={'h4'} fw={"bold"}>
Dokter & Tenaga Medis
</Text>
<Box px={{ base: 'md', md: 100 }}>
<Card radius="xl" p="lg" withBorder style={{ backdropFilter: 'blur(6px)' }}>
<Stack gap="sm">
<Title order={2}>{nama}</Title>
<Group gap="md" wrap="wrap">
<Group gap="xs">
<ThemeIcon variant="light" radius="xl"><IconMapPin size={18} /></ThemeIcon>
<Text>{alamat}</Text>
</Group>
<Group gap="xs">
<ThemeIcon variant="light" radius="xl"><IconDeviceLandlinePhone size={18} /></ThemeIcon>
<Text>{kontak.telepon}</Text>
<CopyButton value={kontak.telepon}>
{({ copied, copy }) => (
<Tooltip label={copied ? 'Disalin' : 'Salin nomor'}>
<ActionIcon variant="subtle" onClick={copy}>{copied ? <IconCheck size={18} /> : <IconCopy size={18} />}</ActionIcon>
</Tooltip>
)}
</CopyButton>
</Group>
<Group gap="xs">
<ThemeIcon variant="light" radius="xl"><IconBrandWhatsapp size={18} /></ThemeIcon>
<Text>{kontak.whatsapp}</Text>
<CopyButton value={kontak.whatsapp}>
{({ copied, copy }) => (
<Tooltip label={copied ? 'Disalin' : 'Salin WhatsApp'}>
<ActionIcon variant="subtle" onClick={copy}>{copied ? <IconCheck size={18} /> : <IconCopy size={18} />}</ActionIcon>
</Tooltip>
)}
</CopyButton>
</Group>
<Group gap="xs">
<ThemeIcon variant="light" radius="xl"><IconMail size={18} /></ThemeIcon>
<Text>{kontak.email}</Text>
<CopyButton value={kontak.email}>
{({ copied, copy }) => (
<Tooltip label={copied ? 'Disalin' : 'Salin email'}>
<ActionIcon variant="subtle" onClick={copy}>{copied ? <IconCheck size={18} /> : <IconCopy size={18} />}</ActionIcon>
</Tooltip>
)}
</CopyButton>
</Group>
</Group>
<Group gap="xs" mt="sm" wrap="wrap">
<Chip defaultChecked radius="xl" variant="light" icon={<IconStethoscope size={16} />}>Layanan Medis</Chip>
<Chip radius="xl" variant="light" icon={<IconUsersGroup size={16} />}>Ramah Keluarga</Chip>
<Chip radius="xl" variant="light" icon={<IconWallet size={16} />}>Pembayaran Non-Tunai</Chip>
</Group>
</Stack>
</Card>
</Box>
<Box px={{ base: 'md', md: 100 }}>
<Grid gutter="lg">
<Grid.Col span={{ base: 12, md: 8 }}>
<Card radius="xl" p="lg" withBorder>
<Stack gap="md">
<Group justify="space-between" align="center">
<Title order={3}>Informasi Umum</Title>
<Badge variant="gradient" gradient={{ from: 'cyan', to: 'blue' }} radius="xl">Terverifikasi</Badge>
</Group>
<Divider />
<Box pb={10}>
<Table highlightOnHover withTableBorder withColumnBorders>
<Group gap="xl" align="start">
<Stack gap={2}>
<Text c="dimmed" fz="sm">Nama Fasilitas</Text>
<Text fw={600}>{nama}</Text>
</Stack>
<Stack gap={2}>
<Text c="dimmed" fz="sm">Jam Operasional</Text>
<Text fw={600}>{jam}</Text>
</Stack>
</Group>
<Divider />
<Title order={4}>Layanan Unggulan</Title>
{Array.isArray(layananUnggulan) && layananUnggulan.length > 0 ? (
<List spacing="xs" icon={<ThemeIcon variant="light" radius="xl"><IconArrowRight size={16} /></ThemeIcon>}>
{layananUnggulan.map((x: any, idx: number) => (
<ListItem key={idx}>
<Group gap="xs" wrap="wrap">
<Text fw={600}>{x?.nama || 'Layanan'}</Text>
{x?.keterangan && <Badge variant="light" radius="sm">{x.keterangan}</Badge>}
</Group>
</ListItem>
))}
</List>
) : (
<Paper withBorder radius="md" p="md">
<Group gap="sm">
<IconMoodEmpty />
<Text>Tidak ada layanan unggulan yang tercatat.</Text>
</Group>
</Paper>
)}
<Divider />
<Title order={4}>Peta Lokasi</Title>
<AspectRatio ratio={16 / 9}>
<iframe src={lokasi.mapsEmbed} style={{ border: 0, width: '100%', height: '100%', borderRadius: 16 }} loading="lazy" aria-label="Peta Lokasi" />
</AspectRatio>
</Stack>
</Card>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 4 }}>
<Stack gap="lg">
<Card radius="xl" p="lg" withBorder>
<Stack gap="md">
<Title order={4}>Kontak Cepat</Title>
<Group gap="sm" wrap="wrap">
<Button variant="light" leftSection={<IconDeviceLandlinePhone size={18} />} component="a" href={`tel:${kontak.telepon}`} aria-label="Hubungi Telepon">Telepon</Button>
<Button variant="light" leftSection={<IconBrandWhatsapp size={18} />} component="a" href={`https://wa.me/${kontak.whatsapp.replace(/\D/g, '')}`} target="_blank" aria-label="Hubungi WhatsApp">WhatsApp</Button>
<Button variant="light" leftSection={<IconMail size={18} />} component="a" href={`mailto:${kontak.email}`} aria-label="Kirim Email">Email</Button>
</Group>
<Anchor target="_blank" underline="hover">Kunjungi situs resmi</Anchor>
</Stack>
</Card>
<Card radius="xl" p="lg" withBorder>
<Stack gap="md">
<Title order={4}>Dokter & Tenaga Medis</Title>
<Table highlightOnHover withTableBorder withColumnBorders stickyHeader stickyHeaderOffset={0} aria-label="Tabel Dokter">
<TableThead>
<TableTr>
<TableTh fz={'h4'}>Nama</TableTh>
<TableTh fz={'h4'}>Spesialisasi</TableTh>
<TableTh fz={'h4'}>Jadwal</TableTh>
<TableTh>Nama</TableTh>
<TableTh>Spesialisasi</TableTh>
<TableTh>Jadwal</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{state.findUnique.data?.dokterdantenagamedis ? (
{tenaga ? (
<TableTr>
<TableTd>{state.findUnique.data.dokterdantenagamedis.name}</TableTd>
<TableTd>{state.findUnique.data.dokterdantenagamedis.specialist}</TableTd>
<TableTd>{state.findUnique.data.dokterdantenagamedis.jadwal}</TableTd>
<TableTd><Group gap="xs"><IconUser size={16} /><Text>{tenaga?.name || '-'}</Text></Group></TableTd>
<TableTd>{tenaga?.specialist || '-'}</TableTd>
<TableTd>{tenaga?.jadwal || '-'}</TableTd>
</TableTr>
) : (
<TableTr>
<TableTd colSpan={3} style={{ textAlign: 'center' }}>Tidak ada data dokter/tenaga medis</TableTd>
<TableTd colSpan={3}>
<Group justify="center" gap="xs" c="dimmed">
<IconSearch size={18} />
<Text>Tidak ada data tenaga medis.</Text>
</Group>
</TableTd>
</TableTr>
)}
</TableTbody>
</Table>
</Box>
{/* Fasilitas Pendukung */}
<Text fz={'h4'} fw={"bold"}>
Fasilitas Pendukung
</Text>
</Stack>
</Card>
</Stack>
</Grid.Col>
</Grid>
</Box>
<Box px={{ base: 'md', md: 100 }}>
<Grid gutter="lg">
<Grid.Col span={{ base: 12, md: 8 }}>
<Card radius="xl" p="lg" withBorder>
<Stack gap="md">
<Title order={3}>Fasilitas Pendukung</Title>
<Divider />
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: state.findUnique.data?.fasilitaspendukung?.content }} />
{/* Dokter */}
<Text fz={'h4'} fw={"bold"}>
Layanan & Tarif
</Text>
{fasilitasPendukungHtml ? (
<Text fz="md" style={{ lineHeight: 1.7 }} dangerouslySetInnerHTML={{ __html: fasilitasPendukungHtml }} />
) : (
<Paper withBorder radius="md" p="md">
<Group gap="sm">
<IconMoodEmpty />
<Text>Belum ada informasi fasilitas pendukung.</Text>
</Group>
</Paper>
)}
</Stack>
</Card>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 4 }}>
<Card radius="xl" p="lg" withBorder>
<Stack gap="md">
<Title order={3}>Layanan & Tarif</Title>
<Divider />
<Table highlightOnHover withTableBorder withColumnBorders>
<Table highlightOnHover withTableBorder withColumnBorders aria-label="Tabel Layanan dan Tarif">
<TableThead>
<TableTr>
<TableTh fz={'h4'}>Layanan</TableTh>
<TableTh fz={'h4'}>Tarif</TableTh>
<TableTh>Layanan</TableTh>
<TableTh>Tarif</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>{state.findUnique?.data?.tarifdanlayanan.layanan}</TableTd>
<TableTd>{state.findUnique?.data?.tarifdanlayanan.tarif}</TableTd>
</TableTr>
{tarif ? (
<TableTr>
<TableTd>{tarif?.layanan || '-'}</TableTd>
<TableTd>{formatRupiah(tarif?.tarif)}</TableTd>
</TableTr>
) : (
<TableTr>
<TableTd colSpan={2}>
<Group justify="center" gap="xs" c="dimmed">
<IconSearch size={18} />
<Text>Tidak ada data tarif.</Text>
</Group>
</TableTd>
</TableTr>
)}
</TableTbody>
</Table>
<Text fz={'h6'} pb={10} fw={"bold"}>
* Gratis dengan BPJS Kesehatan
</Text>
{/* Prosedur Pendaftaran */}
<Text fz={'h4'} fw={"bold"}>
Prosedur Pendaftaran
</Text>
<Divider />
<List type='ordered' pb={10} >
<ListItem fz={'h4'}>Pendaftaran dibuka pukul 07:30 WITA</ListItem>
<ListItem fz={'h4'}>Bawa KTP dan Kartu BPJS (jika ada)</ListItem>
<ListItem fz={'h4'}>Ambil nomor antrian di loket pendaftaran</ListItem>
<ListItem fz={'h4'}>Pengunjung baru wajib mengisi formulir pendaftaran</ListItem>
<ListItem fz={'h4'}>Pendaftaran online tersedia melalui aplikasi S-Sehat</ListItem>
</List>
<Paper p={'lg'} bg={colors['blue-button-trans']}>
<Text fz={'h3'} c={colors['white-1']} fw={'bold'}>Kontak Darurat</Text>
<Text fz={'h4'} c={colors['white-1']} fw={"bold"}>
Telepon : <Text span fz={'h4'}>(0361) 123456</Text>
</Text>
<Text fz={'h4'} c={colors['white-1']} fw={"bold"}>
WhatsApp : <Text span fz={'h4'}>081234567890</Text>
</Text>
<Text fz={'h4'} c={colors['white-1']} fw={"bold"}>
Email : <Text span fz={'h4'}>puskesmasabiansemal3@gmail.com</Text>
</Text>
</Paper>
{/* <Paper p={'lg'} w={{ base: "100%", md: "100%" }}>
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3945.272172359321!2d115.21836257533302!3d-8.569807186941553!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2dd23e9d99b9395f%3A0xb002795fdcb33b30!2sUPTD%20Puskesmas%20Abiansemal%20III!5e0!3m2!1sid!2sid!4v1744792682341!5m2!1sid!2sid" width="600" height="450" style={{ border: 2, width: "100%", borderRadius: 10 }} loading="lazy"></iframe>
</Paper>
{gratisBpjs && (
<Group gap="xs">
<ThemeIcon variant="light" radius="xl"><IconCheck size={18} /></ThemeIcon>
<Text fw={600}>Gratis dengan BPJS Kesehatan</Text>
</Group>
)}
<Group>
<Button fz={'lg'} bg={colors['blue-button']}>
Download Brosur Layanan (PDF)
</Button>
</Group> */}
<Button variant="gradient" gradient={{ from: 'blue', to: 'cyan' }} leftSection={<IconFileDownload size={18} />}>Unduh Brosur (PDF)</Button>
</Group>
</Stack>
</Box>
</Paper>
</Stack>
</Card>
</Grid.Col>
</Grid>
</Box>
<Box px={{ base: 'md', md: 100 }} pb="xl">
<Paper radius="xl" p="lg" withBorder>
<Stack gap="md">
<Title order={3}>Prosedur Pendaftaran</Title>
<Divider />
<List type="ordered" spacing="xs" icon={<ThemeIcon variant="light" radius="xl"><IconArrowRight size={16} /></ThemeIcon>}>
<ListItem>Pendaftaran dibuka pukul 07.30 WITA</ListItem>
<ListItem>Bawa KTP dan Kartu BPJS (jika ada)</ListItem>
<ListItem>Ambil nomor antrian di loket pendaftaran</ListItem>
<ListItem>Pengunjung baru mengisi formulir pendaftaran</ListItem>
<ListItem>Pendaftaran online tersedia melalui aplikasi S-Sehat</ListItem>
</List>
</Stack>
</Paper>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,50 +1,100 @@
'use client'
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Anchor, Box, Divider, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { Anchor, Badge, Box, Card, Divider, Group, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import { IconMapPin, IconClock, IconArrowRight } from '@tabler/icons-react';
function FasilitasKesehatanPage() {
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan)
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
const router = useRouter();
useShallowEffect(() => {
state.findMany.load()
}, [])
state.findMany.load();
}, []);
if(!state.findMany.data){
if (!state.findMany.data) {
return (
<Box py={10}>
<Skeleton h={500} />
<Box py="xl" px="md">
<Stack gap="md">
<Skeleton height={80} radius="lg" />
<Skeleton height={80} radius="lg" />
<Skeleton height={80} radius="lg" />
</Stack>
</Box>
)
);
}
return (
<Box>
<Paper p={'xl'} h={'112vh'} bg={colors['white-trans-1']}>
<Stack gap={'xs'}>
<Text ta={'center'} fw={"bold"} fz={'h3'} c={colors['blue-button']}>Fasilitas Kesehatan</Text>
{state.findMany.data.map((item) => (
<Box key={item.id}>
<Text fz={'h4'} fw={'bold'} c={colors['blue-button']}>
{item.name}
</Text>
<Text fz={'h4'}>
{item.informasiumum.alamat}
</Text>
<Text fz={'h4'}>
{item.informasiumum.jamOperasional}
</Text>
<Anchor onClick={() => router.push(`/darmasaba/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/${item.id}`)} c={colors['blue-button']} variant='transparent'>
<Text c={colors['blue-button']} fz={'h4'}>
Detail {'>>'}
</Text>
</Anchor>
</Box>
))}
<Divider color={colors['blue-button']} px={'xl'} />
<Paper p="xl" radius="xl" shadow="md" h="100%" bg="white">
<Stack gap="lg">
<Text ta="center" fw={700} fz="32px" c={colors['blue-button']}>
Fasilitas Kesehatan
</Text>
<Divider size="sm" color={colors['blue-button']} />
<Stack gap="lg">
{state.findMany.data.map((item) => (
<Card
key={item.id}
withBorder
radius="xl"
shadow="sm"
p="lg"
style={{
background: 'linear-gradient(135deg, #fdfdfd, #f7faff)',
transition: 'transform 0.2s ease, box-shadow 0.2s ease',
}}
onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.transform = 'translateY(-4px)';
(e.currentTarget as HTMLElement).style.boxShadow = '0 8px 20px rgba(0,0,0,0.08)';
}}
onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.transform = 'translateY(0px)';
(e.currentTarget as HTMLElement).style.boxShadow = '0 4px 12px rgba(0,0,0,0.05)';
}}
>
<Stack gap="sm">
<Group justify="space-between" align="center">
<Text fw={700} fz="lg" c={colors['blue-button']}>
{item.name}
</Text>
<Badge color="blue" radius="sm" variant="light" fz="xs">
Aktif
</Badge>
</Group>
<Group gap="xs">
<IconMapPin size={18} stroke={1.5} color={colors['blue-button']} />
<Text fz="sm" c="dimmed">
{item.informasiumum.alamat}
</Text>
</Group>
<Group gap="xs">
<IconClock size={18} stroke={1.5} color={colors['blue-button']} />
<Text fz="sm" c="dimmed">
{item.informasiumum.jamOperasional}
</Text>
</Group>
<Anchor
onClick={() =>
router.push(
`/darmasaba/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/${item.id}`
)
}
c={colors['blue-button']}
fz="sm"
fw={600}
style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}
>
Lihat Detail
<IconArrowRight size={16} stroke={1.5} />
</Anchor>
</Stack>
</Card>
))}
</Stack>
</Stack>
</Paper>
</Box>

View File

@@ -2,10 +2,30 @@
import jadwalkegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { ActionIcon, Box, Button, CheckIcon, Combobox, ComboboxChevron, ComboboxOption, ComboboxOptions, ComboboxTarget, Divider, Group, InputBase, InputPlaceholder, Paper, Skeleton, Stack, Text, Textarea, TextInput, useCombobox } from '@mantine/core';
import {
ActionIcon,
Box,
Button,
Combobox,
ComboboxChevron,
ComboboxOption,
ComboboxOptions,
ComboboxTarget,
Divider,
Group,
InputBase,
InputPlaceholder,
Paper,
Skeleton,
Stack,
Text,
Textarea,
TextInput,
useCombobox
} from '@mantine/core';
import { DateInput } from '@mantine/dates';
import { useShallowEffect } from '@mantine/hooks';
import { IconCalendar } from '@tabler/icons-react';
import { IconCalendar, IconDownload, IconPhone, IconMail, IconUser } from '@tabler/icons-react';
import { useParams } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
@@ -17,6 +37,7 @@ const layanan = [
'Konsultasi Gizi',
'Pemeriksaan Kesehatan'
];
function Page() {
const combobox = useCombobox({
onDropdownClose: () => combobox.resetSelectedOption(),
@@ -30,192 +51,160 @@ function Page() {
});
const [value, setValue] = useState<string | null>('');
const [dateInputOpened, setDateInputOpened] = useState(false);
const params = useParams();
const state = useProxy(jadwalkegiatanState);
useShallowEffect(() => {
state.findUnique.load(params?.id as string);
}, []);
const options = layanan.map((item) => (
<ComboboxOption value={item} key={item} active={item === value}>
<Group gap="xs">
{item === value && <CheckIcon size={12} />}
{item === value && <IconUser size={14} stroke={2} />}
<span>{item}</span>
</Group>
</ComboboxOption>
));
const [dateInputOpened, setDateInputOpened] = useState(false);
const pickerControl = (
<ActionIcon onClick={() => setDateInputOpened(true)} variant="subtle" color="gray">
<ActionIcon onClick={() => setDateInputOpened(true)} variant="light" color="blue">
<IconCalendar size={18} />
</ActionIcon>
);
const params = useParams()
const state = useProxy(jadwalkegiatanState)
useShallowEffect(() => {
state.findUnique.load(params?.id as string)
}, [])
if (!state.findUnique.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
<Stack py="xl" px="lg">
<Skeleton h={500} radius="lg" />
</Stack>
)
);
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Stack pos="relative" bg={colors.Bg} py="xl" gap="lg">
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'}>
<Paper radius={10}>
<Box style={{ borderTopLeftRadius: 10, borderTopRightRadius: 10 }} bg={colors['blue-button']}>
<Text p={'md'} fz={{ base: "h3", md: "h2" }} c={colors['white-1']} fw={"bold"}>
<Stack gap="lg">
<Paper radius="xl" shadow="md">
<Box
style={{ borderTopLeftRadius: 16, borderTopRightRadius: 16 }}
bg={colors['blue-button']}
>
<Text p="md" fz={{ base: "h3", md: "h2" }} c={colors['white-1']} fw="bold">
Detail & Pendaftaran Kegiatan
</Text>
</Box>
<Box p={'md'} >
<Stack gap={'xs'}>
{/* Informasi Kegiatan */}
<Text fz={'h4'} fw={"bold"}>
Informasi Kegiatan
</Text>
<Divider />
<Text fz={'h4'} fw={"bold"}>
Nama Kegiatan : <Text span fz={'h4'}>{state.findUnique.data.informasijadwalkegiatan.name}</Text>
</Text>
<Text fz={'h4'} fw={"bold"}>
Tanggal : <Text span fz={'h4'}>{state.findUnique.data.informasijadwalkegiatan.tanggal}</Text>
</Text>
<Text fz={'h4'} fw={"bold"}>
Waktu : <Text span fz={'h4'}>{state.findUnique.data.informasijadwalkegiatan.waktu}</Text>
</Text>
<Text pb={10} fz={'h4'} fw={"bold"}>
Lokasi : <Text span fz={'h4'}>{state.findUnique.data.informasijadwalkegiatan.lokasi}</Text>
</Text>
{/* Deskripsi Kegiatan */}
<Text fz={'h4'} fw={"bold"}>
Deskripsi Kegiatan
</Text>
<Divider />
<Text pb={10} ta={'justify'} fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.deskripsijadwalkegiatan.deskripsi }} />
{/* Layanan Yang Tersedia */}
<Text fz={'h4'} fw={"bold"}>
Layanan Yang Tersedia
</Text>
<Divider />
<Text pb={10} ta={'justify'} fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.layananjadwalkegiatan.content }} />
{/* Syarat dan Ketentuan */}
<Text fz={'h4'} fw={"bold"}>
Syarat dan Ketentuan
</Text>
<Divider />
<Text pb={10} ta={'justify'} fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.syaratketentuanjadwalkegiatan.content }} />
{/* Dokumen yang Perlu Dibawa */}
<Text fz={'h4'} fw={"bold"}>
Dokumen yang Perlu Dibawa
</Text>
<Divider />
<Text pb={10} ta={'justify'} fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.dokumenjadwalkegiatan.content }} />
{/* Pendaftaran */}
<Text fz={'h4'} fw={"bold"}>
Pendaftaran
</Text>
<Divider />
<Stack gap={'xs'} pb={20}>
<TextInput
styles={{ label: { fontSize: '16px', fontWeight: 'bold' }, }}
w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}
label="Nama Balita"
placeholder='Masukkan nama balita'
/>
<DateInput
styles={{ label: { fontSize: '16px', fontWeight: 'bold' }, }}
placeholder='dd/mm/yyyy'
w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}
label="Tanggal Lahir"
popoverProps={{ opened: dateInputOpened, onChange: setDateInputOpened }}
rightSection={pickerControl}
/>
<TextInput
styles={{ label: { fontSize: '16px', fontWeight: 'bold' }, }}
w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}
label="Nama Orang Tua / Wali"
placeholder='Masukkan nama orang tua / wali'
/>
<TextInput
styles={{ label: { fontSize: '16px', fontWeight: 'bold' }, }}
w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}
label="No. Telepon"
placeholder='Masukkan no. telepon'
/>
<TextInput
styles={{ label: { fontSize: '16px', fontWeight: 'bold' }, }}
w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}
label="Alamat"
placeholder='Masukkan alamat lengkap'
/>
{/* Layanan */}
<Text fz={'16px'} fw={"bold"}>
Layananan Yang Dibutuhkan
</Text>
<Box
w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}
>
<Combobox
store={combobox}
resetSelectionOnOptionHover
withinPortal={false}
onOptionSubmit={(val) => {
setValue(val);
combobox.updateSelectedOptionIndex('active');
}}
>
<ComboboxTarget targetType="button">
<InputBase
component="button"
type="button"
pointer
rightSection={<ComboboxChevron />}
rightSectionPointerEvents="none"
onClick={() => combobox.toggleDropdown()}
>
{value || <InputPlaceholder>Layanan</InputPlaceholder>}
</InputBase>
</ComboboxTarget>
<Combobox.Dropdown>
<ComboboxOptions>{options}</ComboboxOptions>
</Combobox.Dropdown>
</Combobox>
</Box>
<Textarea
pb={10}
styles={{ label: { fontSize: '16px', fontWeight: 'bold' }, }} w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}
label="Catatan Khusus (Opsional)"
placeholder='Masukkan catatan jika ada'
/>
<Button
w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}
fz={'md'} bg={colors['blue-button']}>
Daftar Sekarang
</Button>
<Box p="lg">
<Stack gap="xl">
<Stack gap="sm">
<Text fz="lg" fw="bold">Informasi Kegiatan</Text>
<Divider />
<Text fz="md" fw="bold">Nama Kegiatan: <Text span>{state.findUnique.data.informasijadwalkegiatan.name}</Text></Text>
<Text fz="md" fw="bold">Tanggal: <Text span>{state.findUnique.data.informasijadwalkegiatan.tanggal}</Text></Text>
<Text fz="md" fw="bold">Waktu: <Text span>{state.findUnique.data.informasijadwalkegiatan.waktu}</Text></Text>
<Text fz="md" fw="bold">Lokasi: <Text span>{state.findUnique.data.informasijadwalkegiatan.lokasi}</Text></Text>
</Stack>
<Paper p={'lg'} bg={colors['blue-button-trans']}>
<Text fz={'h3'} c={colors['white-1']} fw={'bold'}>Informasi Kontak</Text>
<Text fz={'h4'} c={colors['white-1']} fw={"bold"}>
Penanggung Jawab : <Text span fz={'h4'}>Bidan Komang Ayu</Text>
</Text>
<Text fz={'h4'} c={colors['white-1']} fw={"bold"}>
Telepon : <Text span fz={'h4'}>081234567890</Text>
</Text>
<Text fz={'h4'} c={colors['white-1']} fw={"bold"}>
Email : <Text span fz={'h4'}>puskesmasabiansemal3@gmail.com</Text>
</Text>
<Stack gap="sm">
<Text fz="lg" fw="bold">Deskripsi Kegiatan</Text>
<Divider />
<Text ta="justify" fz="md" dangerouslySetInnerHTML={{ __html: state.findUnique.data.deskripsijadwalkegiatan.deskripsi }} />
</Stack>
<Stack gap="sm">
<Text fz="lg" fw="bold">Layanan yang Tersedia</Text>
<Divider />
<Text ta="justify" fz="md" dangerouslySetInnerHTML={{ __html: state.findUnique.data.layananjadwalkegiatan.content }} />
</Stack>
<Stack gap="sm">
<Text fz="lg" fw="bold">Syarat & Ketentuan</Text>
<Divider />
<Text ta="justify" fz="md" dangerouslySetInnerHTML={{ __html: state.findUnique.data.syaratketentuanjadwalkegiatan.content }} />
</Stack>
<Stack gap="sm">
<Text fz="lg" fw="bold">Dokumen yang Perlu Dibawa</Text>
<Divider />
<Text ta="justify" fz="md" dangerouslySetInnerHTML={{ __html: state.findUnique.data.dokumenjadwalkegiatan.content }} />
</Stack>
<Stack gap="sm">
<Text fz="lg" fw="bold">Formulir Pendaftaran</Text>
<Divider />
<Stack gap="md">
<TextInput label="Nama Balita" placeholder="Masukkan nama balita" size="md" />
<DateInput
label="Tanggal Lahir"
placeholder="dd/mm/yyyy"
size="md"
w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}
popoverProps={{ opened: dateInputOpened, onChange: setDateInputOpened }}
rightSection={pickerControl}
/>
<TextInput label="Nama Orang Tua / Wali" placeholder="Masukkan nama orang tua / wali" size="md" />
<TextInput label="Nomor Telepon" placeholder="Masukkan nomor telepon" size="md" />
<TextInput label="Alamat" placeholder="Masukkan alamat lengkap" size="md" />
<Box w={{ base: '100%', md: '85%', lg: '75%', xl: '50%' }}>
<Text fz="sm" fw="bold" pb={4}>Pilih Layanan</Text>
<Combobox
store={combobox}
resetSelectionOnOptionHover
withinPortal={false}
onOptionSubmit={(val) => {
setValue(val);
combobox.updateSelectedOptionIndex('active');
}}
>
<ComboboxTarget targetType="button">
<InputBase
component="button"
type="button"
pointer
rightSection={<ComboboxChevron />}
rightSectionPointerEvents="none"
onClick={() => combobox.toggleDropdown()}
>
{value || <InputPlaceholder>Pilih layanan</InputPlaceholder>}
</InputBase>
</ComboboxTarget>
<Combobox.Dropdown>
<ComboboxOptions>{options}</ComboboxOptions>
</Combobox.Dropdown>
</Combobox>
</Box>
<Textarea label="Catatan Khusus (Opsional)" placeholder="Masukkan catatan jika ada" size="md" />
<Button size="md" radius="lg" bg={colors['blue-button']}>
Daftar Sekarang
</Button>
</Stack>
</Stack>
<Paper p="lg" radius="md" bg={colors['blue-button-trans']} shadow="sm">
<Stack gap="xs">
<Text fz="lg" c={colors['white-1']} fw="bold">Informasi Kontak</Text>
<Group gap="xs">
<IconUser size={18} color="white" />
<Text fz="md" c={colors['white-1']}>Penanggung Jawab: <Text span fw="bold">Bidan Komang Ayu</Text></Text>
</Group>
<Group gap="xs">
<IconPhone size={18} color="white" />
<Text fz="md" c={colors['white-1']}>081234567890</Text>
</Group>
<Group gap="xs">
<IconMail size={18} color="white" />
<Text fz="md" c={colors['white-1']}>puskesmasabiansemal3@gmail.com</Text>
</Group>
</Stack>
</Paper>
<Group>
<Button fz={'lg'} bg={'green'}>
Download Jadwal Posyandu 2025 (PDF)
<Button size="md" radius="lg" leftSection={<IconDownload size={18} />} color="green">
Unduh Jadwal Posyandu 2025 (PDF)
</Button>
</Group>
</Stack>
@@ -227,5 +216,4 @@ function Page() {
);
}
export default Page;

View File

@@ -1,53 +1,101 @@
'use client'
import jadwalkegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import colors from '@/con/colors';
import { Anchor, Box, Divider, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { Box, Button, Card, Divider, Group, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconChevronRight, IconClockHour4, IconMapPin } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
function JadwalKegiatanPage() {
const state = useProxy(jadwalkegiatanState)
const state = useProxy(jadwalkegiatanState);
const router = useRouter();
useShallowEffect(()=> {
state.findMany.load()
}, [])
useShallowEffect(() => {
state.findMany.load();
}, []);
if(!state.findMany.data){
if (!state.findMany.data) {
return (
<Box py={10}>
<Skeleton h={500} />
<Box py="lg">
<Skeleton h={500} radius="lg" />
</Box>
)
);
}
return (
<Box>
<Paper p={'xl'} h={'112vh'} bg={colors['white-trans-1']}>
<Stack gap={'xs'}>
<Text ta={'center'} fw={"bold"} fz={'h3'} c={colors['blue-button']}>Jadwal Kegiatan</Text>
{state.findMany.data.map((item) => (
<Box key={item.id}>
<Text fz={'h4'} fw={'bold'}>
{item.informasijadwalkegiatan.name}
<Paper p="xl" bg={colors['white-trans-1']} radius="xl" shadow="md" h="auto" mih="100vh">
<Stack gap="lg">
<Text ta="center" fw={700} fz="32px" c={colors['blue-button']}>
Jadwal Kegiatan Warga
</Text>
{state.findMany.data.length === 0 ? (
<Box py="xl" ta="center">
<Text fz="lg" c="dimmed">
Belum ada jadwal kegiatan yang tersedia
</Text>
<Text fz={'h4'}>
{item.informasijadwalkegiatan.waktu}
</Text>
<Text fz={'h4'}>
{item.informasijadwalkegiatan.lokasi}
</Text>
<Text fz={'h6'} fw={'bold'} c={colors['blue-button']}>
{item.informasijadwalkegiatan.tanggal}
</Text>
<Anchor onClick={()=> router.push(`/darmasaba/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/${item.id}`)} c={colors['blue-button']} variant='transparent'>
<Text c={colors['blue-button']} fz={'h4'} >
Detail & Pendaftaran
</Text>
</Anchor>
</Box>
))}
<Divider color={colors['blue-button']} px={'xl'} />
) : (
state.findMany.data.map((item) => (
<Card
key={item.id}
withBorder
radius="xl"
shadow="sm"
p="lg"
style={{ backdropFilter: 'blur(8px)' }}
>
<Stack gap="sm">
<Group justify="space-between">
<Text fw={700} fz="xl">
{item.informasijadwalkegiatan.name}
</Text>
<Text fw={600} fz="sm" c={colors['blue-button']}>
{item.informasijadwalkegiatan.tanggal}
</Text>
</Group>
<Group gap="xs">
<IconClockHour4 size={18} color={colors['blue-button']} />
<Text fz="sm">{item.informasijadwalkegiatan.waktu}</Text>
</Group>
<Group gap="xs">
<IconMapPin size={18} color={colors['blue-button']} />
<Text fz="sm">{item.informasijadwalkegiatan.lokasi}</Text>
</Group>
<Divider my="sm" />
<Group justify="flex-end">
<Button
variant="light"
radius="lg"
size="sm"
rightSection={<IconChevronRight size={18} />}
onClick={() =>
router.push(
`/darmasaba/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/${item.id}`
)
}
styles={{
root: {
background: colors['blue-button'],
color: 'white',
boxShadow: '0 0 12px rgba(0, 123, 255, 0.4)',
transition: 'all 0.2s ease',
},
}}
>
Lihat Detail & Daftar
</Button>
</Group>
</Stack>
</Card>
))
)}
</Stack>
</Paper>
</Box>

View File

@@ -1,100 +1,169 @@
'use client'
import infoWabahPenyakit from '@/app/admin/(dashboard)/_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit';
import colors from '@/con/colors';
import { Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import {
Box,
Center,
Grid,
GridCol,
Image,
Pagination,
Paper,
SimpleGrid,
Skeleton,
Stack,
Text,
TextInput,
Badge,
HoverCard,
Divider,
Group,
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconSearch } from '@tabler/icons-react';
import { IconSearch, IconInfoCircle } from '@tabler/icons-react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
function Page() {
const state = useProxy(infoWabahPenyakit)
const [search, setSearch] = useState('')
const state = useProxy(infoWabahPenyakit);
const [search, setSearch] = useState('');
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => {
load(page, 3, search)
}, [page, search])
load(page, 6, search);
}, [page, search]);
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
<Box py="xl" px={{ base: 'md', md: 100 }}>
<Skeleton h={500} radius="lg" />
</Box>
)
);
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Stack pos="relative" bg={colors.Bg} py="xl" gap="lg">
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Grid align='center' px={{ base: 'md', md: 100 }}>
<GridCol span={{ base: 12, md: 9 }}>
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Info Wabah / Penyakit
<Grid align="center" px={{ base: 'md', md: 100 }}>
<GridCol span={{ base: 12, md: 8 }}>
<Text
fz={{ base: '1.8rem', md: '2.8rem' }}
c={colors['blue-button']}
fw="bold"
lh={1.2}
>
Informasi Wabah & Penyakit
</Text>
<Text fz="md" c="dimmed" mt={4}>
Dapatkan informasi terbaru mengenai wabah dan penyakit yang sedang
diawasi.
</Text>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<GridCol span={{ base: 12, md: 4 }}>
<TextInput
radius={"lg"}
placeholder='Cari Info Wabah / Penyakit'
radius="xl"
placeholder="Cari nama penyakit atau wabah..."
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w={{ base: "50%", md: "100%" }}
w="100%"
size="md"
/>
</GridCol>
</Grid>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'}>
<SimpleGrid
cols={{
base: 1,
md: 3,
}}
>
{data.map((v, k) => {
return (
<Paper key={k} p={'xl'} bg={colors['white-trans-1']}>
<Stack gap={'xs'}>
<Box>
<Text fw={"bold"} fz={'h3'} c={colors['blue-button']}>{v.name}</Text>
<Image w={330} h={200} fit='contain' pt={5} src={v.image.link} alt={v.name} />
<Text fz={'h4'} fw={'bold'} >
{v.name}
<Box px={{ base: 'md', md: 100 }}>
{data.length === 0 ? (
<Center py="6xl">
<Stack align="center" gap="sm">
<IconInfoCircle size={50} color={colors['blue-button']} />
<Text fz="lg" fw={500} c="dimmed">
Tidak ada data yang cocok dengan pencarian Anda.
</Text>
</Stack>
</Center>
) : (
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg">
{data.map((v, k) => (
<Paper
key={k}
radius="lg"
shadow="md"
p="lg"
withBorder
bg={colors['white-trans-1']}
style={{
transition: 'transform 200ms ease, box-shadow 200ms ease',
}}
>
<Stack gap="sm">
<Image
radius="md"
h={180}
src={v.image.link}
alt={v.name}
fit="cover"
/>
<Group justify="space-between" mt="sm">
<Text fw={700} fz="lg" c={colors['blue-button']}>
{v.name}
</Text>
<Badge color="blue" variant="light" radius="sm">
Wabah
</Badge>
</Group>
<Text fz="sm" c="dimmed">
Diposting: 12 Februari 2025 · Dinas Kesehatan
</Text>
<Divider />
<Text fz="sm" lh={1.5}>
{v.deskripsiSingkat}
</Text>
<HoverCard shadow="md" position="bottom" radius="md" width={300}>
<HoverCard.Target>
<Text
fz="sm"
fw={500}
c={colors['blue-button']}
style={{ cursor: 'pointer' }}
>
Lihat detail lengkap
</Text>
<Text fz={'h6'} pb={10}>
Diposting: 12 Februari 2025 | Dinas Kesehatan
</Text>
<Text fz={'h4'} pb={10}>
{v.deskripsiSingkat}
</Text>
<Text fz={'h4'} pb={10} dangerouslySetInnerHTML={{ __html: v.deskripsiLengkap }} />
</Box>
</Stack>
</Paper>
)
})}
</HoverCard.Target>
<HoverCard.Dropdown>
<Text
fz="sm"
lh={1.6}
dangerouslySetInnerHTML={{
__html: v.deskripsiLengkap,
}}
/>
</HoverCard.Dropdown>
</HoverCard>
</Stack>
</Paper>
))}
</SimpleGrid>
</Stack>
)}
</Box>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
{totalPages > 1 && (
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
radius="xl"
size="md"
mt="lg"
/>
</Center>
)}
</Stack>
);
}

View File

@@ -1,106 +1,150 @@
'use client'
import colors from '@/con/colors';
import { Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import BackButton from '../../desa/layanan/_com/BackButto';
import { useProxy } from 'valtio/utils';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import { useShallowEffect } from '@mantine/hooks';
import {
Box,
Center,
Grid,
GridCol,
Image,
Pagination,
Paper,
SimpleGrid,
Skeleton,
Stack,
Text,
TextInput,
Tooltip,
Badge,
} from '@mantine/core';
import { IconSearch, IconPhone } from '@tabler/icons-react';
import BackButton from '../../desa/layanan/_com/BackButto';
import kontakDarurat from '@/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat';
import { IconSearch } from '@tabler/icons-react';
import colors from '@/con/colors';
function Page() {
const state = useProxy(kontakDarurat)
const [search, setSearch] = useState('')
const state = useProxy(kontakDarurat);
const [search, setSearch] = useState('');
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => {
load(page, 3, search)
}, [page, search])
load(page, 6, search);
}, [page, search]);
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
<Box py="xl" px={{ base: 'md', md: 100 }}>
<Skeleton height={500} radius="lg" />
</Box>
)
);
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Stack pos="relative" bg={colors.Bg} py="xl" gap="lg">
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Grid align='center' px={{ base: 'md', md: 100 }}>
<GridCol span={{ base: 12, md: 9 }}>
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Penanganan Darurat
<Grid align="center" px={{ base: 'md', md: 100 }} gutter="lg">
<GridCol span={{ base: 12, md: 8 }}>
<Text fz={{ base: '2rem', md: '2.8rem' }} c={colors['blue-button']} fw={800}>
Kontak Darurat
</Text>
<Text c="dimmed" fz="md" mt={4}>
Hubungi layanan penting dengan cepat dan mudah
</Text>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<TextInput
radius={"lg"}
placeholder='Cari Penanganan Darurat'
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w={{ base: "50%", md: "100%" }}
/>
<GridCol span={{ base: 12, md: 4 }}>
<Tooltip label="Cari berdasarkan nama atau deskripsi" withArrow>
<TextInput
radius="xl"
size="md"
placeholder="Cari kontak darurat..."
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w="100%"
/>
</Tooltip>
</GridCol>
</Grid>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'}>
<SimpleGrid
pb={10}
cols={{
base: 1,
md: 3,
}}>
{data.map((v, k) => {
return (
<Paper radius={10} key={k} bg={colors["white-trans-1"]}>
<Stack gap={'xs'}>
<Center py={40}>
<Image
src={v.image.link}
alt={v.name}
w={200}
h={200}
fit='contain'
/>
</Center>
<Box px={'lg'}>
<Box pb={20}>
<Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}>
{v.name}
</Text>
<Box px={10}>
<Text fz={"h4"} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
</Box>
</Box>
</Box>
</Stack>
</Paper>
)
})}
<Box px={{ base: 'md', md: 100 }}>
{data.length === 0 ? (
<Center mih={300}>
<Stack align="center" gap="xs">
<IconSearch size={50} color={colors['blue-button']} />
<Text fz="lg" fw={600} c={colors['blue-button']}>
Tidak ada kontak ditemukan
</Text>
<Text fz="sm" c="dimmed">
Coba kata kunci lain untuk pencarian
</Text>
</Stack>
</Center>
) : (
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="xl" mt="lg">
{data.map((v, k) => (
<Paper
key={k}
radius="xl"
shadow="md"
withBorder
p="lg"
bg={colors['white-trans-1']}
style={{
transition: 'all 200ms ease',
cursor: 'pointer',
}}
>
<Stack align="center" gap="sm">
<Image
src={v.image.link}
alt={v.name}
w={140}
h={140}
fit="contain"
radius="md"
/>
<Text ta="center" fw={700} fz="lg" c={colors['blue-button']}>
{v.name}
</Text>
<Text fz="sm" c="dimmed" ta="center" lineClamp={3}>
<span dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
</Text>
<Badge
color="blue"
leftSection={<IconPhone size={14} />}
variant="light"
mt="sm"
>
Panggil Sekarang
</Badge>
</Stack>
</Paper>
))}
</SimpleGrid>
</Stack>
)}
</Box>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
{totalPages > 1 && (
<Center mt="xl">
<Pagination
value={page}
onChange={(newPage) => load(newPage, 6, search)}
total={totalPages}
radius="xl"
size="md"
styles={{
control: {
borderRadius: '999px',
},
}}
/>
</Center>
)}
</Stack>
);
}

View File

@@ -1,108 +1,162 @@
'use client'
import colors from '@/con/colors';
import { Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import BackButton from '../../desa/layanan/_com/BackButto';
import { useProxy } from 'valtio/utils';
import { useState } from 'react';
import { useShallowEffect } from '@mantine/hooks';
import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat';
import { IconSearch } from '@tabler/icons-react';
import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat'
import colors from '@/con/colors'
import {
Badge,
Box,
Center,
Grid,
GridCol,
Image,
Pagination,
Paper,
SimpleGrid,
Skeleton,
Stack,
Text,
TextInput,
Tooltip
} from '@mantine/core'
import { useShallowEffect } from '@mantine/hooks'
import { IconSearch } from '@tabler/icons-react'
import { useState } from 'react'
import { useProxy } from 'valtio/utils'
import BackButton from '../../desa/layanan/_com/BackButto'
function Page() {
const state = useProxy(penangananDarurat)
const [search, setSearch] = useState('')
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
const { data, page, totalPages, loading, load } = state.findMany
useShallowEffect(() => {
load(page, 3, search)
load(page, 6, search)
}, [page, search])
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
<Box py="xl" px={{ base: 'md', md: 100 }}>
<Skeleton h={500} radius="lg" />
</Box>
)
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Stack bg={colors.Bg} py="xl" gap="xl" pos="relative">
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Grid align='center' px={{ base: 'md', md: 100 }}>
<Grid align="center" px={{ base: 'md', md: 100 }} mb="lg">
<GridCol span={{ base: 12, md: 9 }}>
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
<Text fz={{ base: 30, md: 40 }} c={colors['blue-button']} fw={800} lh={1.2}>
Penanganan Darurat
</Text>
<Text fz="md" c="dimmed" mt={4}>
Informasi cepat dan jelas untuk situasi darurat kesehatan
</Text>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<TextInput
radius={"lg"}
placeholder='Cari Penanganan Darurat'
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w={{ base: "50%", md: "100%" }}
/>
<Tooltip label="Ketik kata kunci untuk mencari penanganan darurat">
<TextInput
radius="lg"
placeholder="Cari penanganan darurat..."
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w="100%"
/>
</Tooltip>
</GridCol>
</Grid>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'}>
<Box px={{ base: 'md', md: 100 }}>
{data.length === 0 ? (
<Center py={100}>
<Stack align="center" gap="xs">
<Text fz="lg" fw={600} c={colors['blue-button']}>
Tidak ada data ditemukan
</Text>
<Text fz="sm" c="dimmed">
Coba gunakan kata kunci lain
</Text>
</Stack>
</Center>
) : (
<SimpleGrid
pb={10}
cols={{
base: 1,
md: 3,
}}>
{data.map((v, k) => {
return (
<Paper radius={10} key={k} bg={colors["white-trans-1"]}>
<Stack gap={'xs'}>
<Center py={40}>
<Image
src={v.image.link}
alt={v.name}
w={200}
h={200}
fit='contain'
cols={{ base: 1, sm: 2, md: 3 }}
spacing="xl"
verticalSpacing="xl"
pb={20}
>
{data.map((v, k) => (
<Paper
key={k}
radius="xl"
p="md"
shadow="sm"
withBorder
bg={colors['white-trans-1']}
style={{ transition: 'all 0.3s ease' }}
>
<Stack align="center" gap="md">
<Center>
<Image
src={v.image.link}
alt={v.name}
w={160}
h={160}
fit="contain"
radius="md"
/>
</Center>
<Stack gap={4} w="100%">
<Text
fz="lg"
fw={700}
c={colors['blue-button']}
ta="center"
lineClamp={2}
>
{v.name}
</Text>
<Box>
<Text
fz="sm"
c="dimmed"
lineClamp={4}
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
/>
</Center>
<Box px={'lg'}>
<Box pb={20}>
<Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}>
{v.name}
</Text>
<Box px={10}>
<Text fz={"h4"} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
</Box>
</Box>
</Box>
</Stack>
</Paper>
)
})}
<Badge radius="md" color="blue" variant="light" mt="sm">
Darurat
</Badge>
</Stack>
</Paper>
))}
</SimpleGrid>
</Stack>
)}
</Box>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
{totalPages > 1 && (
<Center mt="xl">
<Pagination
value={page}
onChange={(newPage) => load(newPage, 6, search)}
total={totalPages}
size="lg"
radius="xl"
styles={{
control: {
border: `1px solid ${colors['blue-button']}`,
},
}}
/>
</Center>
)}
</Stack>
);
)
}
export default Page;
export default Page

View File

@@ -1,119 +1,166 @@
'use client'
import colors from "@/con/colors";
import { Box, Center, Flex, Image, List, ListItem, Pagination, Paper, SimpleGrid, Skeleton, Spoiler, Stack, Text, TextInput } from "@mantine/core";
import BackButton from "../../desa/layanan/_com/BackButto";
// import { useTransitionRouter } from "next-view-transitions";
import posyandustate from "@/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu";
import colors from "@/con/colors";
import { Badge, Box, Center, Flex, Image, List, ListItem, Pagination, Paper, SimpleGrid, Skeleton, Spoiler, Stack, Text, TextInput } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { useProxy } from "valtio/utils";
import { IconCalendar, IconInfoCircle, IconPhone, IconSearch } from "@tabler/icons-react";
import { useState } from "react";
import { IconSearch } from "@tabler/icons-react";
import { useProxy } from "valtio/utils";
import BackButton from "../../desa/layanan/_com/BackButto";
export default function Page() {
const state = useProxy(posyandustate)
// const router = useTransitionRouter()
const [search, setSearch] = useState("")
const state = useProxy(posyandustate);
const [search, setSearch] = useState("");
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => {
load(page, 6, search);
}, [page, search]);
useShallowEffect(() => {
load(page, 3, search)
}, [page, search])
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
</Box>
)
}
if (loading || !data) {
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
<Flex mt={10} justify={"space-between"} align={"center"}>
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Posyandu Darmasaba
</Text>
<TextInput
placeholder="Cari Posyandu"
radius="lg"
leftSection={<IconSearch size={20} />}
w={{ base: "25%", md: "30%" }}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
</Flex>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'}>
<SimpleGrid
pb={10}
cols={{
base: 1,
md: 3,
}}>
{data?.map((v, k) => {
return (
<Paper key={k} p={"xl"} bg={colors["white-trans-1"]}>
<Stack gap={'xs'}>
<Text c={colors["blue-button"]} fw={"bold"} fz={"h3"}>
{v.name}
</Text>
<Center>
<Image src={v.image.link} alt="" />
</Center>
<Text fz={'h4'}>
No.Telp : {v.nomor}
</Text>
<Box>
<Text fz={'h4'}>
Jadwal Pelayanan
</Text>
<Text fz={'h4'} dangerouslySetInnerHTML={{ __html: v.jadwalPelayanan }} />
</Box>
<Spoiler
maxHeight={60} // tinggi maksimum sebelum di-collapse
showLabel="Read more"
hideLabel="Read less"
>
<Text fz={'h4'} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
</Spoiler>
</Stack>
</Paper>
)
})}
</SimpleGrid>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
<Text fz={'h4'} fw={"bold"}>
Pelayanan Posyandu
</Text>
<List type="ordered">
<ListItem fz={'h4'}>Penimbangan bayi dan balita</ListItem>
<ListItem fz={'h4'}>Pemantuan status gizi</ListItem>
<ListItem fz={'h4'}>Imunisasi dasar lengkap</ListItem>
<ListItem fz={'h4'}>Konseling</ListItem>
<ListItem fz={'h4'}>Pemantuan kesehatan ibu hamil</ListItem>
</List>
</Stack>
</Box>
</Stack>
<Box py="xl" px={{ base: "md", md: 100 }}>
<Skeleton h={500} radius="lg" />
</Box>
);
}
)
}
return (
<Stack pos="relative" bg={colors.Bg} py="xl" gap="xl">
<Box px={{ base: "md", md: 100 }}>
<BackButton />
<Flex mt="md" justify="space-between" align="center" wrap="wrap" gap="md">
<Text
ta="left"
fz={{ base: "1.8rem", md: "2.5rem" }}
c={colors["blue-button"]}
fw="bold"
>
Posyandu Desa Darmasaba
</Text>
<TextInput
placeholder="Cari posyandu berdasarkan nama..."
aria-label="Pencarian Posyandu"
radius="xl"
size="md"
leftSection={<IconSearch size={20} />}
w={{ base: "100%", md: "35%" }}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
</Flex>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Stack gap="xl">
<SimpleGrid
pb="lg"
cols={{ base: 1, sm: 2, md: 3 }}
spacing="lg"
>
{data?.map((v, k) => (
<Paper
key={k}
p="xl"
radius="lg"
shadow="md"
withBorder
bg={colors["white-trans-1"]}
style={{
transition: "transform 0.2s ease, box-shadow 0.2s ease",
}}
onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.transform = "translateY(-4px)";
(e.currentTarget as HTMLElement).style.boxShadow =
"0 8px 24px rgba(0,0,0,0.12)";
}}
onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.transform = "translateY(0)";
(e.currentTarget as HTMLElement).style.boxShadow =
"0 4px 12px rgba(0,0,0,0.08)";
}}
>
<Stack gap="sm">
<Flex justify="space-between" align="center">
<Text c={colors["blue-button"]} fw="bold" fz="lg" lineClamp={1}>
{v.name}
</Text>
<Badge color="blue" variant="light" size="sm" radius="sm">
Aktif
</Badge>
</Flex>
<Center>
<Image
src={v.image.link}
alt={`Gambar ${v.name}`}
radius="md"
w="100%"
h={180}
fit="cover"
/>
</Center>
<Flex align="center" gap="xs">
<IconPhone size={18} stroke={1.5} />
<Text fz="sm" c="dimmed">
{v.nomor || "Tidak tersedia"}
</Text>
</Flex>
<Flex align="center" gap="xs">
<IconCalendar size={18} stroke={1.5} />
<Text fz="sm" c="dimmed">
Jadwal:{" "}
<span dangerouslySetInnerHTML={{ __html: v.jadwalPelayanan }} />
</Text>
</Flex>
<Spoiler
maxHeight={70}
showLabel="Lihat selengkapnya"
hideLabel="Sembunyikan"
transitionDuration={300}
>
<Text
fz="sm"
lh={1.5}
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
/>
</Spoiler>
</Stack>
</Paper>
))}
</SimpleGrid>
{totalPages > 1 && (
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
radius="lg"
size="md"
withControls
mt="md"
/>
</Center>
)}
<Stack gap="sm">
<Flex align="center" gap="xs">
<IconInfoCircle size={22} color={colors["blue-button"]} />
<Text fz="lg" fw="bold" c={colors["blue-button"]}>
Layanan Utama Posyandu
</Text>
</Flex>
<List spacing="xs" size="sm" center>
<ListItem>Penimbangan bayi dan balita</ListItem>
<ListItem>Pemantauan status gizi</ListItem>
<ListItem>Imunisasi dasar lengkap</ListItem>
<ListItem>Konseling kesehatan</ListItem>
<ListItem>Pemantauan kesehatan ibu hamil</ListItem>
</List>
</Stack>
</Stack>
</Box>
</Stack>
);
}

View File

@@ -1,7 +1,7 @@
'use client'
import programKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/program-kesehatan/programKesehatan';
import colors from '@/con/colors';
import { Box, Center, Group, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { Box, Center, Group, Image, Loader, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconCalendar, IconUser } from '@tabler/icons-react';
import { useParams } from 'next/navigation';
@@ -18,45 +18,75 @@ function Page() {
if (!state.findUnique.data) {
return (
<div>
<Skeleton h={500} />
</div>
<Center mih="60vh">
<Stack align="center" gap="sm">
<Loader color={colors["blue-button"]} size="lg" />
<Text c="dimmed" fz="sm">Memuat data program kesehatan...</Text>
</Stack>
</Center>
)
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Stack pos="relative" bg={colors.Bg} py="xl" gap="xl">
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Paper px={{ base: 'md', md: 100 }} radius={10} bg={colors["white-trans-1"]}>
<Stack gap={'xs'}>
<Center my={20}>
<Image radius={"lg"} src={state.findUnique.data.image?.link} alt="" />
<Paper
px={{ base: 'md', md: 100 }}
py="xl"
radius="xl"
shadow="md"
bg={colors["white-trans-1"]}
>
<Stack gap="lg">
<Center>
{state.findUnique.data.image?.link ? (
<Image
radius="xl"
src={state.findUnique.data.image?.link}
alt={state.findUnique.data.name}
w="100%"
maw={800}
fit="cover"
/>
) : (
<Skeleton h={300} w="100%" radius="xl" />
)}
</Center>
<Box px={'lg'}>
<Box>
<Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}>
{state.findUnique.data.name}
</Text>
<Text ta={'justify'} fz={'h4'} dangerouslySetInnerHTML={{ __html: state.findUnique.data.deskripsi }}></Text>
</Box>
<Group py={20}>
<Group gap="xs">
<IconCalendar size={18} />
<Text size="sm">
{state.findUnique.data.createdAt ? new Date(state.findUnique.data.createdAt).toLocaleDateString('id-ID', {
day: 'numeric',
month: 'long',
year: 'numeric',
}) : 'No date available'}
</Text>
</Group>
<Group gap="xs">
<IconUser size={18} />
<Text size="sm">Admin Desa</Text>
</Group>
</Group>
<Box>
<Text pb="sm" c={colors["blue-button"]} fw="bold" fz={{ base: 24, md: 32 }} lh={1.2}>
{state.findUnique.data.name}
</Text>
<Text
ta="justify"
fz="md"
lh={1.6}
dangerouslySetInnerHTML={{ __html: state.findUnique.data.deskripsi }}
/>
</Box>
<Group gap="xl">
<Group gap="xs">
<Tooltip label="Tanggal dibuat" withArrow>
<IconCalendar size={20} stroke={1.5} />
</Tooltip>
<Text size="sm" c="dimmed">
{state.findUnique.data.createdAt
? new Date(state.findUnique.data.createdAt).toLocaleDateString('id-ID', {
day: 'numeric',
month: 'long',
year: 'numeric',
})
: 'Tanggal tidak tersedia'}
</Text>
</Group>
<Group gap="xs">
<Tooltip label="Dibuat oleh" withArrow>
<IconUser size={20} stroke={1.5} />
</Tooltip>
<Text size="sm" c="dimmed">Admin Desa</Text>
</Group>
</Group>
</Stack>
</Paper>
</Stack>

View File

@@ -1,7 +1,31 @@
'use client'
import colors from "@/con/colors";
import { Box, Button, Center, Grid, GridCol, Group, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from "@mantine/core";
import { IconBarbell, IconCalendar, IconOld, IconSearch, IconUser, IconUsersGroup } from "@tabler/icons-react";
import {
Box,
Button,
Center,
Grid,
GridCol,
Group,
Image,
Pagination,
Paper,
SimpleGrid,
Skeleton,
Stack,
Text,
TextInput,
Tooltip,
Transition,
} from "@mantine/core";
import {
IconBarbell,
IconCalendar,
IconOld,
IconSearch,
IconUser,
IconUsersGroup,
} from "@tabler/icons-react";
import BackButton from "../../desa/layanan/_com/BackButto";
import { useProxy } from "valtio/utils";
import programKesehatan from "@/app/admin/(dashboard)/_state/kesehatan/program-kesehatan/programKesehatan";
@@ -9,104 +33,134 @@ import { useState } from "react";
import { useShallowEffect } from "@mantine/hooks";
import { useRouter } from "next/navigation";
const data2 = [
const manfaatProgram = [
{
id: 1,
icon: <IconBarbell size={50} color={colors['BG-trans']} />,
title: "Menjaga Kesehatan Tubuh",
desc: "Program kesehatan desa dirancang untuk memelihara kesehatan fisik masyarakat melalui aktivitas rutin, pemeriksaan kesehatan berkala, dan edukasi gaya hidup sehat.",
icon: <IconBarbell size={40} color="#fff" />,
title: "Menjaga Tubuh Bugar",
desc: "Meningkatkan kesehatan fisik masyarakat melalui olahraga rutin, pemeriksaan berkala, dan edukasi gaya hidup sehat.",
},
{
id: 2,
icon: <IconUsersGroup size={50} color={colors['BG-trans']} />,
title: "Mempererat Kebersamaan",
desc: "Kegiatan kesehatan komunal menjadi wadah interaksi sosial yang memperkuat ikatan antar warga desa, menumbuhkan rasa kepedulian dan gotong royong.",
icon: <IconUsersGroup size={40} color="#fff" />,
title: "Menguatkan Kebersamaan",
desc: "Menciptakan interaksi sosial sehat, mempererat solidaritas, dan memperkuat budaya gotong royong antar warga.",
},
{
id: 3,
icon: <IconOld size={50} color={colors['BG-trans']} />,
title: "Medukung Lansia Aktif",
desc: "Program khusus bagi lansia membantu menjaga kebugaran, mengurangi risiko penyakit degeneratif, dan menciptakan komunitas pendukung untuk kehidupan yang sehat dan bahagia.",
icon: <IconOld size={40} color="#fff" />,
title: "Dukungan untuk Lansia",
desc: "Membantu lansia tetap aktif, sehat, dan bahagia melalui kegiatan komunitas yang aman dan menyenangkan.",
},
]
];
export default function Page() {
const state = useProxy(programKesehatan)
const router = useRouter()
const [search, setSearch] = useState('')
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
const state = useProxy(programKesehatan);
const router = useRouter();
const [search, setSearch] = useState("");
const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => {
load(page, 3, search)
}, [page, search])
load(page, 3, search);
}, [page, search]);
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
<Box py="xl" px={{ base: "md", md: 100 }}>
<Skeleton h={500} radius="lg" />
</Box>
)
);
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
<Stack bg={colors.Bg} py="xl" gap="xl">
<Box px={{ base: "md", md: 100 }}>
<BackButton />
</Box>
<Grid px={{ base: 'md', md: 100 }} align="center">
<GridCol span={{ base: 12, md: 9 }}>
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Program Kesehatan Unggulan
<Grid px={{ base: "md", md: 100 }} align="center" gutter="lg">
<GridCol span={{ base: 12, md: 8 }}>
<Text
fz={{ base: "2rem", md: "2.8rem" }}
c={colors["blue-button"]}
fw="bold"
>
Program Kesehatan Desa
</Text>
<Text fz="lg" c="dimmed" mt="xs">
Temukan berbagai program kesehatan untuk mendukung kualitas hidup
masyarakat Darmasaba.
</Text>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<GridCol span={{ base: 12, md: 4 }}>
<TextInput
placeholder='Cari Program Kesehatan'
placeholder="Cari program kesehatan..."
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w={{ base: "50%", md: "100%" }}
radius="lg"
size="md"
aria-label="Pencarian program kesehatan"
/>
</GridCol>
</Grid>
<Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'}>
<SimpleGrid
pb={10}
cols={{
base: 1,
md: 3,
}}>
{data.map((v, k) => {
return (
<Paper radius={10} key={k} bg={colors["white-trans-1"]}>
<Stack gap={'xs'}>
<Center>
<Image src={v.image?.link} alt="" style={{ borderRadius: '14px 14px 0 0' }} />
</Center>
<Box px={'lg'}>
<Box>
<Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}>
{v.name}
</Text>
<Text ta={'justify'} fz={'h4'} dangerouslySetInnerHTML={{ __html: v.deskripsi }}></Text>
</Box>
<Box py={15} onClick={() => router.push(`/darmasaba/kesehatan/program-kesehatan/${v.id}`)}>
<Button fw={'bold'} fz={'h5'} c={colors["blue-button"]} bg={colors["BG-trans"]}>Detail Program</Button>
</Box>
<Group py={20}>
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg" pb="xl">
{data.map((v, k) => (
<Transition
key={k}
mounted
transition="fade-up"
duration={400}
timingFunction="ease"
>
{(styles) => (
<Paper
style={styles}
radius="xl"
withBorder
bg="white"
shadow="md"
className="hover-scale"
>
<Stack gap="md">
<Image
src={v.image?.link}
alt={v.name}
radius="xl"
height={180}
fit="cover"
/>
<Box px="lg" pb="lg">
<Text
fw="bold"
fz="xl"
c={colors["blue-button"]}
mb="xs"
>
{v.name}
</Text>
<Text
fz="sm"
c="dimmed"
lineClamp={3}
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
/>
<Group justify="space-between" mt="md">
<Group gap="xs">
<IconCalendar size={18} />
<Text size="sm">
{v.createdAt ? new Date(v.createdAt).toLocaleDateString('id-ID', {
day: 'numeric',
month: 'long',
year: 'numeric',
}) : 'No date available'}
{v.createdAt
? new Date(v.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)
: "Tanggal tidak tersedia"}
</Text>
</Group>
<Group gap="xs">
@@ -114,61 +168,99 @@ export default function Page() {
<Text size="sm">Admin Desa</Text>
</Group>
</Group>
<Tooltip label="Lihat detail program" withArrow>
<Button
mt="lg"
fullWidth
radius="lg"
size="md"
fw="bold"
variant="gradient"
gradient={{ from: colors["blue-button"], to: "#4dabf7" }}
onClick={() =>
router.push(
`/darmasaba/kesehatan/program-kesehatan/${v.id}`
)
}
>
Lihat Detail
</Button>
</Tooltip>
</Box>
</Stack>
</Paper>
)
})}
</SimpleGrid>
)}
</Transition>
))}
</SimpleGrid>
{totalPages > 1 && (
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
onChange={(newPage) => load(newPage)}
total={totalPages}
mt="md"
mb="md"
size="lg"
radius="xl"
styles={{
control: {
borderRadius: "50%",
boxShadow: "0 0 10px rgba(0,0,0,0.1)",
},
}}
/>
</Center>
</Stack>
)}
</Box>
<Box py={10} px={{ base: "md", md: 100 }}>
<Box pb={10}>
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
<Box px={{ base: "md", md: 100 }} py="xl">
<Stack gap="sm" mb="lg">
<Text
fz={{ base: "2rem", md: "2.5rem" }}
c={colors["blue-button"]}
fw="bold"
>
Manfaat Program Kesehatan
</Text>
<Text fz={{ base: "h4", md: "h3" }} >
Program kesehatan di Desa Darmasaba memiliki peran penting dalam meningkatkan kesejahteraan masyarakat.
<Text fz="lg" c="dimmed" maw={700}>
Program kesehatan Desa Darmasaba berperan penting dalam meningkatkan
kesejahteraan dan kualitas hidup warganya.
</Text>
</Box>
<SimpleGrid
pb={30}
cols={{
base: 1,
md: 3,
}}>
{data2.map((v, k) => {
return (
<Paper key={k} px={"xl"} py={80} bg={colors['white-trans-1']} c={colors['blue-button']}>
<Stack justify='space-between' >
<Group justify='center'>
<Paper p={'xl'} radius={'100'} bg={colors['blue-button']}>
<Center >
{v.icon}
</Center>
</Paper>
</Group>
<Text ta={"center"} fw={"bold"} fz={"h3"}>
{v.title}
</Text>
<Text ta={"center"} fz={'h4'}>
{v.desc}
</Text>
</Stack>
</Paper>
)
})}
</Stack>
<SimpleGrid cols={{ base: 1, md: 3 }} spacing="lg">
{manfaatProgram.map((v) => (
<Paper
key={v.id}
px="xl"
py="xl"
radius="xl"
shadow="sm"
withBorder
bg="white"
className="hover-glow"
>
<Stack align="center" gap="md">
<Paper
p="lg"
radius="50%"
shadow="md"
bg={colors["blue-button"]}
className="pulse-icon"
>
<Center>{v.icon}</Center>
</Paper>
<Text ta="center" fw="bold" fz="xl" c={colors["blue-button"]}>
{v.title}
</Text>
<Text ta="center" fz="sm" c="dimmed">
{v.desc}
</Text>
</Stack>
</Paper>
))}
</SimpleGrid>
</Box>
</Stack>
)
);
}

View File

@@ -1,10 +1,11 @@
'use client'
import puskesmasState from '@/app/admin/(dashboard)/_state/kesehatan/puskesmas/puskesmas';
import colors from '@/con/colors';
import { BackgroundImage, Box, Grid, GridCol, Paper, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core';
import { BackgroundImage, Box, Grid, GridCol, Paper, SimpleGrid, Skeleton, Stack, Text, Title, Tooltip, Badge, Divider, Group } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useParams } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import { IconClock, IconMapPin, IconPhone, IconMail, IconBuildingHospital } from '@tabler/icons-react';
import BackButton from '../../../desa/layanan/_com/BackButto';
function Page() {
@@ -17,95 +18,117 @@ function Page() {
if (!state.findUnique.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
<Stack py="xl" px={{ base: 'md', md: 100 }}>
<Skeleton h={500} radius="lg" />
<Stack gap="sm">
<Skeleton h={20} w="40%" />
<Skeleton h={20} w="60%" />
<Skeleton h={20} w="50%" />
</Stack>
</Stack>
)
}
const data = state.findUnique.data
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Stack pos="relative" bg={colors.Bg} py="xl" gap={32}>
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Stack gap={'lg'} px={{ base: 'md', md: 100 }}>
<Box>
<Paper p={"xl"} bg={colors['white-trans-1']}>
<Box pb={30}>
<BackgroundImage
pb={30}
radius={16}
h={{ base: 250, md: 500 }}
src={state.findUnique.data.image.link}
style={{ position: 'relative' }}
<Stack gap="xl" px={{ base: 'md', md: 100 }}>
<Paper p="lg" radius="xl" shadow="md" bg={colors['white-trans-1']} withBorder>
<Box pb="xl">
<BackgroundImage
radius="lg"
h={{ base: 260, md: 480 }}
src={data.image.link}
style={{ position: 'relative', overflow: 'hidden' }}
>
<Box
pos="absolute"
w="100%"
h="100%"
bg={colors.trans.dark[2]}
style={{ borderRadius: 16 }}
/>
<Stack
pos="absolute"
bottom={20}
left={20}
gap={6}
>
<Box
style={{
borderRadius: 16,
zIndex: 0
}}
pos={"absolute"}
w={"100%"}
h={"100%"}
bg={colors.trans.dark[2]}
/>
<Text style={{
position: 'absolute',
bottom: 35,
left: 15,
}} fw={'bold'} fz={{ base: 'md', md: 'h3' }} c={colors['white-1']}>{state.findUnique.data.name}</Text>
<Text style={{
position: 'absolute',
bottom: 10,
left: 15,
}} fw={'bold'} fz={{ base: 'md', md: 'h4' }} c={colors['white-1']}>{state.findUnique.data.alamat}</Text>
</BackgroundImage>
<Grid
py={20}>
<GridCol span={{ base: 12, md: 6 }}>
<Text fw="bold" fz={{ base: 'lg', md: 'h2' }} c={colors['white-1']}>
{data.name}
</Text>
<Group gap={6}>
<IconMapPin size={20} color="white" />
<Text fz={{ base: 'sm', md: 'md' }} c={colors['white-1']}>
{data.alamat}
</Text>
</Group>
</Stack>
</BackgroundImage>
<Grid mt="xl" gutter="xl">
<GridCol span={{ base: 12, md: 6 }}>
<Stack gap="lg">
<Box>
<Stack>
<Title order={3}>Informasi</Title>
<Box>
<Text>Alamat: {state.findUnique.data.alamat}</Text>
<Text>Telepon: {state.findUnique.data.kontak.kontakPuskesmas}</Text>
<Text>Email: {state.findUnique.data.kontak.email}</Text>
</Box>
<Title order={3}>Jam Operasional</Title>
<Box>
<Text pb={10} fz={'h4'} fw={"bold"}>
Senin - Kamis: <Text span fz={'h4'}>{state.findUnique.data?.jam.workDays} - {state.findUnique.data?.jam.weekDays}</Text>
</Text>
</Box>
<Title order={3} mb={10}>Informasi Kontak</Title>
<Stack gap={8}>
<Group gap={8}>
<IconPhone size={18} />
<Text fz="md">{data.kontak.kontakPuskesmas || '-'}</Text>
</Group>
<Group gap={8}>
<IconMail size={18} />
<Text fz="md">{data.kontak.email || '-'}</Text>
</Group>
</Stack>
</Box>
</GridCol>
<GridCol span={{ base: 12, md: 6 }}>
<Box>
<Paper p={"xl"} bg={'#B1C5F2'}>
<SimpleGrid
cols={{
base: 1,
md: 2
}}>
<Paper p={"xl"} bg={colors['white-trans-1']}>
<Text fw={"bold"} fz={'h3'} ta={'center'}>Poliklinik Umum</Text>
<Text ta={'center'} fz={{ base: 45, md: 65 }} fw={'bold'} c={colors['blue-button']}>26</Text>
</Paper>
<Paper p={"xl"} bg={colors['white-trans-1']}>
<Text ta={'center'} fz={'h3'} fw={"bold"}>Poli Gigi</Text>
<Text ta={'center'} fz={{ base: 45, md: 65 }} fw={'bold'} c={colors['blue-button']}>26</Text>
</Paper>
</SimpleGrid>
</Paper>
</Box>
</GridCol>
<Box>
</Box>
</Grid>
</Box>
</Paper>
</Box>
<Divider />
<Box>
<Title order={3} mb={10}>Jam Operasional</Title>
<Group gap={8}>
<IconClock size={18} />
<Text fw="bold" fz="md">
{data.jam.workDays} - {data.jam.weekDays}
</Text>
<Tooltip label="Hari aktif pelayanan puskesmas" position="top" withArrow>
<Badge size="sm" variant="light" color="blue">Aktif</Badge>
</Tooltip>
</Group>
</Box>
</Stack>
</GridCol>
<GridCol span={{ base: 12, md: 6 }}>
<Paper p="xl" radius="lg" bg="linear-gradient(135deg, #EAF0FB, #BFD4F5)" shadow="sm">
<Title order={3} mb="lg" ta="center" c="dark">Layanan Unggulan</Title>
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="lg">
<Paper p="lg" radius="lg" withBorder bg={colors['white-trans-1']} shadow="xs">
<Stack align="center" gap={8}>
<IconBuildingHospital size={36} color={colors['blue-button']} />
<Text fw="bold" fz="lg">Poliklinik Umum</Text>
<Text fz={{ base: 36, md: 48 }} fw="bold" c={colors['blue-button']}>26</Text>
</Stack>
</Paper>
<Paper p="lg" radius="lg" withBorder bg={colors['white-trans-1']} shadow="xs">
<Stack align="center" gap={8}>
<IconBuildingHospital size={36} color={colors['blue-button']} />
<Text fw="bold" fz="lg">Poli Gigi</Text>
<Text fz={{ base: 36, md: 48 }} fw="bold" c={colors['blue-button']}>26</Text>
</Stack>
</Paper>
</SimpleGrid>
</Paper>
</GridCol>
</Grid>
</Box>
</Paper>
</Stack>
</Stack>
);

View File

@@ -1,14 +1,13 @@
'use client'
import puskesmasState from '@/app/admin/(dashboard)/_state/kesehatan/puskesmas/puskesmas';
import colors from '@/con/colors';
import { Anchor, Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import { Anchor, Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput, Badge, Group } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconSearch } from '@tabler/icons-react';
import { IconSearch, IconMapPin, IconPhone, IconMail } from '@tabler/icons-react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
function Page() {
const state = useProxy(puskesmasState)
const [search, setSearch] = useState('')
@@ -22,76 +21,122 @@ function Page() {
} = state.findMany;
useShallowEffect(() => {
load(page, 3, search)
load(page, 6, search)
}, [page, search])
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
<Box py="xl" px={{ base: 'md', md: 100 }}>
<SimpleGrid cols={{ base: 1, md: 3 }} spacing="lg">
{Array.from({ length: 6 }).map((_, i) => (
<Skeleton key={i} height={320} radius="lg" />
))}
</SimpleGrid>
</Box>
)
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Stack pos="relative" bg={colors.Bg} py="xl" gap="lg">
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Grid align='center' px={{ base: 'md', md: 100 }}>
<GridCol span={{ base: 12, md: 9 }}>
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Puskesmas Darmasaba
<Grid align="center" px={{ base: 'md', md: 100 }} mb="md">
<GridCol span={{ base: 12, md: 8 }}>
<Text fz={{ base: "2rem", md: "2.5rem" }} c={colors["blue-button"]} fw="bold">
Daftar Puskesmas
</Text>
<Text fz="sm" c="dimmed">
Temukan informasi lengkap mengenai layanan, kontak, dan lokasi Puskesmas Darmasaba
</Text>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<GridCol span={{ base: 12, md: 4 }}>
<TextInput
radius={"lg"}
placeholder='Cari Puskesmas'
radius="xl"
placeholder="Cari nama puskesmas..."
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w={{ base: "50%", md: "100%" }}
leftSection={<IconSearch size={18} />}
aria-label="Cari Puskesmas"
/>
</GridCol>
</Grid>
<Box px={{ base: "md", md: 100 }}>
<SimpleGrid
cols={{
base: 1,
md: 3,
}}
>
{data.map((v, k) => {
return (
<Paper p={"xl"} bg={colors['white-trans-1']} key={k}>
<Stack gap={"xs"}>
<Text fw={"bold"} fz={"h3"}>{v.name}</Text>
<Box px={{ base: 'md', md: 100 }}>
{data.length === 0 ? (
<Center py="xl">
<Stack align="center" gap="xs">
<Text fz="lg" fw={500} c="dimmed">Tidak ada data ditemukan</Text>
<Text fz="sm" c="dimmed">Coba gunakan kata kunci pencarian yang berbeda</Text>
</Stack>
</Center>
) : (
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg">
{data.map((v, k) => (
<Paper
key={k}
p="lg"
radius="xl"
withBorder
shadow="md"
bg={colors['white-trans-1']}
style={{ transition: 'transform 200ms ease' }}
className="hover:scale-[1.02]"
>
<Stack gap="sm">
<Image
src={v.image.link}
alt={v.name}
radius="md"
height={160}
fit="cover"
/>
<Box>
<Text>Alamat: {v.alamat}</Text>
<Text>Telepon: {v.kontak.kontakPuskesmas}</Text>
<Text>Email: {v.kontak.email}</Text>
</Box>
<Anchor c={colors['blue-button']} href={`/darmasaba/kesehatan/puskesmas/${v.id}`}>Lihat Detail &gt;</Anchor>
<Group justify="space-between">
<Text fw={600} fz="lg" lineClamp={1}>{v.name}</Text>
<Badge color="blue" variant="light" radius="sm" fz="xs">Aktif</Badge>
</Group>
<Stack gap={4}>
<Group gap="xs">
<IconMapPin size={16} />
<Text fz="sm" c="dimmed" lineClamp={2}>{v.alamat}</Text>
</Group>
<Group gap="xs">
<IconPhone size={16} />
<Text fz="sm" c="dimmed">{v.kontak.kontakPuskesmas}</Text>
</Group>
<Group gap="xs">
<IconMail size={16} />
<Text fz="sm" c="dimmed">{v.kontak.email}</Text>
</Group>
</Stack>
<Anchor
href={`/darmasaba/kesehatan/puskesmas/${v.id}`}
fz="sm"
fw={500}
c={colors['blue-button']}
>
Lihat detail
</Anchor>
</Stack>
</Paper>
)
})}
</SimpleGrid>
))}
</SimpleGrid>
)}
</Box>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
{totalPages > 1 && (
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage, 6, search)}
total={totalPages}
size="md"
radius="xl"
mt="lg"
/>
</Center>
)}
</Stack>
);
}