Merge pull request 'nico/10-des-25' (#40) from nico/10-des-25 into staggingweb
Reviewed-on: http://wibugit.wibudev.com/wibu/desa-darmasaba/pulls/40
This commit is contained in:
@@ -11,21 +11,21 @@ function LayoutTabsDetail({ children }: { children: React.ReactNode }) {
|
|||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
label: "Profile Desa",
|
label: "Profil Desa",
|
||||||
value: "profiledesa",
|
value: "profildesa",
|
||||||
href: "/admin/desa/profile/profile-desa",
|
href: "/admin/desa/profil/profil-desa",
|
||||||
icon: <IconUser size={18} stroke={1.8} />
|
icon: <IconUser size={18} stroke={1.8} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Profile Perbekel",
|
label: "Profil Perbekel",
|
||||||
value: "profileperbekel",
|
value: "profilperbekel",
|
||||||
href: "/admin/desa/profile/profile-perbekel",
|
href: "/admin/desa/profil/profil-perbekel",
|
||||||
icon: <IconUsers size={18} stroke={1.8} />
|
icon: <IconUsers size={18} stroke={1.8} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Profile Perbekel Dari Masa Ke Masa",
|
label: "Profil Perbekel Dari Masa Ke Masa",
|
||||||
value: "profile-perbekel-dari-masa-ke-masa",
|
value: "profilperbekeldarimasakemasa",
|
||||||
href: "/admin/desa/profile/profile-perbekel-dari-masa-ke-masa",
|
href: "/admin/desa/profil/profil-perbekel-dari-masa-ke-masa",
|
||||||
icon: <IconCalendar size={18} stroke={1.8} />
|
icon: <IconCalendar size={18} stroke={1.8} />
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -12,22 +12,22 @@ function LayoutTabsEdit({ children }: { children: React.ReactNode }) {
|
|||||||
{
|
{
|
||||||
label: "Sejarah Desa",
|
label: "Sejarah Desa",
|
||||||
value: "sejarahdesa",
|
value: "sejarahdesa",
|
||||||
href: "/admin/desa/profile/edit/sejarah_desa"
|
href: "/admin/desa/profil/edit/sejarah_desa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Visi Misi Desa",
|
label: "Visi Misi Desa",
|
||||||
value: "visimisidesa",
|
value: "visimisidesa",
|
||||||
href: "/admin/desa/profile/edit/visi_misi_desa"
|
href: "/admin/desa/profil/edit/visi_misi_desa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Lambang Desa",
|
label: "Lambang Desa",
|
||||||
value: "lambangdesa",
|
value: "lambangdesa",
|
||||||
href: "/admin/desa/profile/edit/lambang_desa"
|
href: "/admin/desa/profil/edit/lambang_desa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Maskot Desa",
|
label: "Maskot Desa",
|
||||||
value: "maskotdesa",
|
value: "maskotdesa",
|
||||||
href: "/admin/desa/profile/edit/maskot_desa"
|
href: "/admin/desa/profil/edit/maskot_desa"
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const curentTab = tabs.find(tab => tab.href === pathname)
|
const curentTab = tabs.find(tab => tab.href === pathname)
|
||||||
@@ -43,7 +43,7 @@ function Page() {
|
|||||||
const id = params?.id as string;
|
const id = params?.id as string;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
toast.error('ID tidak valid');
|
toast.error('ID tidak valid');
|
||||||
router.push('/admin/desa/profile/profile-desa');
|
router.push('/admin/desa/profil/profil-desa');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ function Page() {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
toast.success('Data berhasil disimpan');
|
toast.success('Data berhasil disimpan');
|
||||||
router.push('/admin/desa/profile/profile-desa');
|
router.push('/admin/desa/profil/profil-desa');
|
||||||
} else {
|
} else {
|
||||||
toast.error('Gagal menyimpan data');
|
toast.error('Gagal menyimpan data');
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ function Page() {
|
|||||||
<Alert icon={<IconAlertCircle size={20} />} color="red" title="Terjadi Kesalahan" radius="md">
|
<Alert icon={<IconAlertCircle size={20} />} color="red" title="Terjadi Kesalahan" radius="md">
|
||||||
{loadError}
|
{loadError}
|
||||||
</Alert>
|
</Alert>
|
||||||
<Button onClick={() => router.push('/admin/desa/profile/profile-desa')} variant="outline">
|
<Button onClick={() => router.push('/admin/desa/profil/profil-desa')} variant="outline">
|
||||||
Kembali ke Halaman Utama
|
Kembali ke Halaman Utama
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -40,7 +40,7 @@ function Page() {
|
|||||||
const id = params?.id as string;
|
const id = params?.id as string;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
toast.error("ID tidak valid");
|
toast.error("ID tidak valid");
|
||||||
router.push("/admin/desa/profile/profile-desa");
|
router.push("/admin/desa/profil/profil-desa");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ function Page() {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
toast.success("Maskot berhasil diperbarui!");
|
toast.success("Maskot berhasil diperbarui!");
|
||||||
router.push("/admin/desa/profile/profile-desa");
|
router.push("/admin/desa/profil/profil-desa");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error update maskot:", error);
|
console.error("Error update maskot:", error);
|
||||||
@@ -50,7 +50,7 @@ function Page() {
|
|||||||
const id = params?.id as string;
|
const id = params?.id as string;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
toast.error('ID tidak valid');
|
toast.error('ID tidak valid');
|
||||||
router.push('/admin/desa/profile/profile-desa');
|
router.push('/admin/desa/profil/profil-desa');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ function Page() {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
toast.success('Data berhasil disimpan');
|
toast.success('Data berhasil disimpan');
|
||||||
router.push('/admin/desa/profile/profile-desa');
|
router.push('/admin/desa/profil/profil-desa');
|
||||||
} else {
|
} else {
|
||||||
toast.error('Gagal menyimpan data');
|
toast.error('Gagal menyimpan data');
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ function Page() {
|
|||||||
{loadError}
|
{loadError}
|
||||||
</Alert>
|
</Alert>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => router.push('/admin/desa/profile/profile-desa')}
|
onClick={() => router.push('/admin/desa/profil/profil-desa')}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
Kembali ke Halaman Utama
|
Kembali ke Halaman Utama
|
||||||
@@ -42,7 +42,7 @@ function Page() {
|
|||||||
const id = params?.id as string;
|
const id = params?.id as string;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
toast.error('ID tidak valid');
|
toast.error('ID tidak valid');
|
||||||
router.push('/admin/desa/profile/profile-desa');
|
router.push('/admin/desa/profil/profil-desa');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ function Page() {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
toast.success('Data berhasil disimpan');
|
toast.success('Data berhasil disimpan');
|
||||||
router.push('/admin/desa/profile/profile-desa');
|
router.push('/admin/desa/profil/profil-desa');
|
||||||
} else {
|
} else {
|
||||||
toast.error('Gagal menyimpan data');
|
toast.error('Gagal menyimpan data');
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ function Page() {
|
|||||||
{loadError}
|
{loadError}
|
||||||
</Alert>
|
</Alert>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => router.push('/admin/desa/profile/profile-desa')}
|
onClick={() => router.push('/admin/desa/profil/profil-desa')}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
Kembali ke Halaman Utama
|
Kembali ke Halaman Utama
|
||||||
@@ -27,7 +27,7 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Title order={2} c={colors['blue-button']}>Preview Profile Desa</Title>
|
<Title order={2} c={colors['blue-button']}>Preview Profil Desa</Title>
|
||||||
|
|
||||||
{/* Sejarah Desa */}
|
{/* Sejarah Desa */}
|
||||||
{sejarah && (
|
{sejarah && (
|
||||||
@@ -42,7 +42,7 @@ function Page() {
|
|||||||
variant="light"
|
variant="light"
|
||||||
leftSection={<IconEdit size={18} stroke={2} />}
|
leftSection={<IconEdit size={18} stroke={2} />}
|
||||||
radius="md"
|
radius="md"
|
||||||
onClick={() => router.push(`/admin/desa/profile/profile-desa/${sejarah.id}/sejarah_desa`)}
|
onClick={() => router.push(`/admin/desa/profil/profil-desa/${sejarah.id}/sejarah_desa`)}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
@@ -87,7 +87,7 @@ function Page() {
|
|||||||
variant="light"
|
variant="light"
|
||||||
leftSection={<IconEdit size={18} stroke={2} />}
|
leftSection={<IconEdit size={18} stroke={2} />}
|
||||||
radius="md"
|
radius="md"
|
||||||
onClick={() => router.push(`/admin/desa/profile/profile-desa/${visiMisi.id}/visi_misi_desa`)}
|
onClick={() => router.push(`/admin/desa/profil/profil-desa/${visiMisi.id}/visi_misi_desa`)}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
@@ -135,7 +135,7 @@ function Page() {
|
|||||||
variant="light"
|
variant="light"
|
||||||
leftSection={<IconEdit size={18} stroke={2} />}
|
leftSection={<IconEdit size={18} stroke={2} />}
|
||||||
radius="md"
|
radius="md"
|
||||||
onClick={() => router.push(`/admin/desa/profile/profile-desa/${lambang.id}/lambang_desa`)}
|
onClick={() => router.push(`/admin/desa/profil/profil-desa/${lambang.id}/lambang_desa`)}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
@@ -180,7 +180,7 @@ function Page() {
|
|||||||
variant="light"
|
variant="light"
|
||||||
leftSection={<IconEdit size={18} stroke={2} />}
|
leftSection={<IconEdit size={18} stroke={2} />}
|
||||||
radius="md"
|
radius="md"
|
||||||
onClick={() => router.push(`/admin/desa/profile/profile-desa/${maskot.id}/maskot_desa`)}
|
onClick={() => router.push(`/admin/desa/profil/profil-desa/${maskot.id}/maskot_desa`)}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
@@ -117,7 +117,7 @@ function EditPerbekelDariMasaKeMasa() {
|
|||||||
|
|
||||||
await state.update.update();
|
await state.update.update();
|
||||||
toast.success('Perbekel dari masa ke masa berhasil diperbarui!');
|
toast.success('Perbekel dari masa ke masa berhasil diperbarui!');
|
||||||
router.push('/admin/desa/profile/profile-perbekel-dari-masa-ke-masa');
|
router.push('/admin/desa/profil/profil-perbekel-dari-masa-ke-masa');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating perbekel dari masa ke masa:', error);
|
console.error('Error updating perbekel dari masa ke masa:', error);
|
||||||
toast.error('Terjadi kesalahan saat memperbarui perbekel dari masa ke masa');
|
toast.error('Terjadi kesalahan saat memperbarui perbekel dari masa ke masa');
|
||||||
@@ -25,7 +25,7 @@ function DetailPerbekelDariMasa() {
|
|||||||
state.delete.byId(selectedId);
|
state.delete.byId(selectedId);
|
||||||
setModalHapus(false);
|
setModalHapus(false);
|
||||||
setSelectedId(null);
|
setSelectedId(null);
|
||||||
router.push("/admin/desa/profile/profile-perbekel-dari-masa-ke-masa");
|
router.push("/admin/desa/profil/profil-perbekel-dari-masa-ke-masa");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ function DetailPerbekelDariMasa() {
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="green"
|
||||||
onClick={() => router.push(`/admin/desa/profile/profile-perbekel-dari-masa-ke-masa/${data.id}/edit`)}
|
onClick={() => router.push(`/admin/desa/profil/profil-perbekel-dari-masa-ke-masa/${data.id}/edit`)}
|
||||||
variant="light"
|
variant="light"
|
||||||
radius="md"
|
radius="md"
|
||||||
size="md"
|
size="md"
|
||||||
@@ -46,7 +46,7 @@ function CreatePerbekelDariMasaKeMasa() {
|
|||||||
state.create.form.imageId = uploaded.id;
|
state.create.form.imageId = uploaded.id;
|
||||||
await state.create.create();
|
await state.create.create();
|
||||||
resetForm();
|
resetForm();
|
||||||
router.push('/admin/desa/profile/profile-perbekel-dari-masa-ke-masa');
|
router.push('/admin/desa/profil/profil-perbekel-dari-masa-ke-masa');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast.error('Gagal menambahkan perbekel dari masa ke masa');
|
toast.error('Gagal menambahkan perbekel dari masa ke masa');
|
||||||
@@ -53,7 +53,7 @@ function ListPerbekelDariMasaKeMasa({ search }: { search: string }) {
|
|||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => router.push('/admin/desa/profile/profile-perbekel-dari-masa-ke-masa/create')}
|
onClick={() => router.push('/admin/desa/profil/profil-perbekel-dari-masa-ke-masa/create')}
|
||||||
>
|
>
|
||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
@@ -90,7 +90,7 @@ function ListPerbekelDariMasaKeMasa({ search }: { search: string }) {
|
|||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
leftSection={<IconDeviceImacCog size={16} />}
|
leftSection={<IconDeviceImacCog size={16} />}
|
||||||
onClick={() => router.push(`/admin/desa/profile/profile-perbekel-dari-masa-ke-masa/${item.id}`)}
|
onClick={() => router.push(`/admin/desa/profil/profil-perbekel-dari-masa-ke-masa/${item.id}`)}
|
||||||
>
|
>
|
||||||
Detail
|
Detail
|
||||||
</Button>
|
</Button>
|
||||||
@@ -25,7 +25,7 @@ function ProfilePerbekel() {
|
|||||||
const id = params?.id as string;
|
const id = params?.id as string;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
toast.error("ID tidak valid");
|
toast.error("ID tidak valid");
|
||||||
router.push("/admin/desa/profile/profile-perbekel");
|
router.push("/admin/desa/profil/profil-perbekel");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ function ProfilePerbekel() {
|
|||||||
const success = await perbekelState.edit.submit()
|
const success = await perbekelState.edit.submit()
|
||||||
if (success) {
|
if (success) {
|
||||||
toast.success("Data berhasil disimpan");
|
toast.success("Data berhasil disimpan");
|
||||||
router.push("/admin/desa/profile/profile-perbekel");
|
router.push("/admin/desa/profil/profil-perbekel");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error update sejarah desa:", error);
|
console.error("Error update sejarah desa:", error);
|
||||||
@@ -41,7 +41,7 @@ function Page() {
|
|||||||
variant="light"
|
variant="light"
|
||||||
leftSection={<IconEdit size={18} stroke={2} />}
|
leftSection={<IconEdit size={18} stroke={2} />}
|
||||||
radius="md"
|
radius="md"
|
||||||
onClick={() => router.push(`/admin/desa/profile/profile-perbekel/${perbekel.id}`)}
|
onClick={() => router.push(`/admin/desa/profil/profil-perbekel/${perbekel.id}`)}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
@@ -91,8 +91,8 @@ export const devBar = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "Desa_1",
|
id: "Desa_1",
|
||||||
name: "Profile",
|
name: "Profil",
|
||||||
path: "/admin/desa/profile/profile-desa"
|
path: "/admin/desa/profil/profil-desa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "Desa_2",
|
id: "Desa_2",
|
||||||
@@ -495,8 +495,8 @@ export const navBar = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "Desa_1",
|
id: "Desa_1",
|
||||||
name: "Profile",
|
name: "Profil",
|
||||||
path: "/admin/desa/profile/profile-desa"
|
path: "/admin/desa/profil/profil-desa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "Desa_2",
|
id: "Desa_2",
|
||||||
@@ -899,8 +899,8 @@ export const role1 = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "Desa_1",
|
id: "Desa_1",
|
||||||
name: "Profile",
|
name: "Profil",
|
||||||
path: "/admin/desa/profile/profile-desa"
|
path: "/admin/desa/profil/profil-desa"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "Desa_2",
|
id: "Desa_2",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
Skeleton,
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
|
Title,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
@@ -41,9 +42,9 @@ export default function DetailInformasiPublikUser() {
|
|||||||
return (
|
return (
|
||||||
<Center py="xl">
|
<Center py="xl">
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<Text fz="lg" fw="bold">
|
<Title order={4} fz={{ base: 'lg', md: 'xl' }} lh={1.3}>
|
||||||
Informasi tidak ditemukan
|
Informasi tidak ditemukan
|
||||||
</Text>
|
</Title>
|
||||||
<Button variant="light" onClick={() => router.push('/informasi-publik')}>
|
<Button variant="light" onClick={() => router.push('/informasi-publik')}>
|
||||||
Kembali ke Daftar
|
Kembali ke Daftar
|
||||||
</Button>
|
</Button>
|
||||||
@@ -75,32 +76,36 @@ export default function DetailInformasiPublikUser() {
|
|||||||
shadow="xs"
|
shadow="xs"
|
||||||
>
|
>
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
<Text
|
{/* MAIN TITLE */}
|
||||||
fz={{ base: 'xl', md: '2xl' }}
|
<Title
|
||||||
fw="bold"
|
order={2}
|
||||||
|
lh={1.2}
|
||||||
ta="center"
|
ta="center"
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
>
|
>
|
||||||
Detail Informasi Publik
|
Detail Informasi Publik
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
|
{/* CONTENT */}
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
|
{/* Jenis Informasi */}
|
||||||
<Box px="lg">
|
<Box px="lg">
|
||||||
<Text fz={{ base: 'md', md: 'lg' }} fw="bold" mb={4}>
|
<Title order={5} fz={{ base: 'lg', md: 'xl' }} lh={1.3} mb={4}>
|
||||||
Jenis Informasi
|
Jenis Informasi
|
||||||
</Text>
|
</Title>
|
||||||
<Text fz={{ base: 'sm', md: 'md' }} c="dimmed">
|
<Text fz={{ base: 'sm', md: 'md' }} lh={1.5} c="black">
|
||||||
{data.jenisInformasi || '-'}
|
{data.jenisInformasi || '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Tanggal Publikasi */}
|
||||||
<Box px="lg">
|
<Box px="lg">
|
||||||
<Text fz={{ base: 'md', md: 'lg' }} fw="bold" mb={4}>
|
<Title order={5} fz={{ base: 'lg', md: 'xl' }} lh={1.3} mb={4}>
|
||||||
Tanggal Publikasi
|
Tanggal Publikasi
|
||||||
</Text>
|
</Title>
|
||||||
<Text fz={{ base: 'sm', md: 'md' }} c="dimmed">
|
<Text fz={{ base: 'sm', md: 'md' }} lh={1.5} c="black">
|
||||||
{data.tanggal
|
{data.tanggal
|
||||||
? new Date(data.tanggal).toLocaleDateString('id-ID', {
|
? new Date(data.tanggal).toLocaleDateString('id-ID', {
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
@@ -111,17 +116,20 @@ export default function DetailInformasiPublikUser() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Deskripsi */}
|
||||||
<Box px="lg">
|
<Box px="lg">
|
||||||
<Text fz={{ base: 'md', md: 'lg' }} fw="bold" mb={4}>
|
<Title order={5} fz={{ base: 'lg', md: 'xl' }} lh={1.3} mb={4}>
|
||||||
Deskripsi
|
Deskripsi
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text
|
<Text
|
||||||
ta={"justify"}
|
ta="justify"
|
||||||
className="prose max-w-none leading-relaxed"
|
|
||||||
dangerouslySetInnerHTML={{ __html: data.deskripsi || '-' }}
|
dangerouslySetInnerHTML={{ __html: data.deskripsi || '-' }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
fz={{ base: 'sm', md: 'md' }}
|
||||||
fz={{ base: 'md', md: 'lg' }}
|
lh={1.6}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
className="prose max-w-none"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ import {
|
|||||||
TableTr,
|
TableTr,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Tooltip
|
Tooltip,
|
||||||
|
Title
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconBrandWhatsapp, IconDeviceImacCog, IconFileInfo, IconMail, IconSearch } from '@tabler/icons-react';
|
import { IconBrandWhatsapp, IconDeviceImacCog, IconFileInfo, IconMail, IconSearch } from '@tabler/icons-react';
|
||||||
@@ -33,7 +34,7 @@ import { useTransitionRouter } from 'next-view-transitions';
|
|||||||
function Page() {
|
function Page() {
|
||||||
const listData = useProxy(daftarInformasiPublik)
|
const listData = useProxy(daftarInformasiPublik)
|
||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
|
const [debouncedSearch] = useDebouncedValue(search, 1000); // 1000ms delay
|
||||||
const router = useTransitionRouter()
|
const router = useTransitionRouter()
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -65,20 +66,49 @@ function Page() {
|
|||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
<Image src="/darmasaba-icon.png" w={{ base: 70, md: 100 }} alt="Logo Desa Darmasaba" loading="lazy" />
|
<Image
|
||||||
|
src="/darmasaba-icon.png"
|
||||||
|
w={{ base: 70, md: 100 }}
|
||||||
|
alt="Logo Desa Darmasaba"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
<Text ta="center" fz={{ base: "1.8rem", md: "2.5rem" }} c={colors["blue-button"]} fw="bold" lh={1.4}>
|
|
||||||
|
<Title
|
||||||
|
order={2}
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: '1.6rem', md: '2.4rem' }}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
lh={1.35}
|
||||||
|
style={{ fontWeight: 700 }}
|
||||||
|
>
|
||||||
Daftar Informasi Publik
|
Daftar Informasi Publik
|
||||||
</Text>
|
</Title>
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Paper p="lg" radius="xl" shadow="sm" withBorder>
|
<Paper p="lg" radius="xl" shadow="sm" withBorder>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Text ta={"center"} fz={{ base: 'lg', md: 'xl' }} fw="bold" c={colors["blue-button"]}>
|
<Title
|
||||||
|
order={4}
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: 'lg', md: 'xl' }}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
lh={1.2}
|
||||||
|
style={{ fontWeight: 700 }}
|
||||||
|
>
|
||||||
Tentang Informasi Publik
|
Tentang Informasi Publik
|
||||||
</Text>
|
</Title>
|
||||||
<Text ta={"center"} fz={{ base: 'sm', md: 'md' }} c="dimmed">
|
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: 'sm', md: 'md' }}
|
||||||
|
c="black"
|
||||||
|
lh={1.6}
|
||||||
|
style={{ maxWidth: 900, margin: '0 auto' }}
|
||||||
|
>
|
||||||
Daftar Informasi Publik Desa Darmasaba adalah kumpulan data yang dapat diakses oleh masyarakat sesuai dengan ketentuan peraturan yang berlaku.
|
Daftar Informasi Publik Desa Darmasaba adalah kumpulan data yang dapat diakses oleh masyarakat sesuai dengan ketentuan peraturan yang berlaku.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -97,8 +127,8 @@ function Page() {
|
|||||||
{data.length === 0 ? (
|
{data.length === 0 ? (
|
||||||
<Center py="xl">
|
<Center py="xl">
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<IconFileInfo size={48} stroke={1.5} color={colors["blue-button"]} />
|
<IconFileInfo size={48} stroke={1.5} color={colors['blue-button']} />
|
||||||
<Text fz="md" c="dimmed">Tidak ada informasi publik yang ditemukan.</Text>
|
<Text fz="md" c="dimmed" lh={1.5}>Tidak ada informasi publik yang ditemukan.</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
@@ -113,27 +143,42 @@ function Page() {
|
|||||||
<TableTh fz="sm" ta="center" w="15%">Aksi</TableTh>
|
<TableTh fz="sm" ta="center" w="15%">Aksi</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
|
|
||||||
<TableTbody bg={colors['white-1']}>
|
<TableTbody bg={colors['white-1']}>
|
||||||
{data.map((item, index) => (
|
{data.map((item, index) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd ta="center">{(page - 1) * 5 + index + 1}</TableTd>
|
<TableTd ta="center">
|
||||||
|
<Text fz="sm" lh={1.4}>
|
||||||
|
{(page - 1) * 5 + index + 1}
|
||||||
|
</Text>
|
||||||
|
</TableTd>
|
||||||
|
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box>
|
<Box>
|
||||||
<Badge variant="light" size="lg" color="blue">
|
<Badge variant="light" size="lg" color="blue">
|
||||||
<Text fw={650} fz={"sm"} c={'blue'} lineClamp={1}>
|
<Text fw={650} fz="sm" c="blue" lineClamp={1} lh={1.2}>
|
||||||
{item.jenisInformasi}
|
{item.jenisInformasi}
|
||||||
</Text>
|
</Text>
|
||||||
</Badge>
|
</Badge>
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
|
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box>
|
<Box>
|
||||||
<Text lineClamp={1} fz="sm" c="dark" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
<Text
|
||||||
|
lineClamp={1}
|
||||||
|
fz={{ base: 'sm', md: 'md' }}
|
||||||
|
c="dark"
|
||||||
|
lh={1.5}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
|
|
||||||
<TableTd ta="center">
|
<TableTd ta="center">
|
||||||
<Box>
|
<Box>
|
||||||
<Text ta={"center"}>
|
<Text ta="center" fz="sm" lh={1.4}>
|
||||||
{item.tanggal ? new Date(item.tanggal).toLocaleDateString('id-ID', {
|
{item.tanggal ? new Date(item.tanggal).toLocaleDateString('id-ID', {
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
@@ -142,6 +187,7 @@ function Page() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
|
|
||||||
<TableTd style={{ textAlign: 'center' }}>
|
<TableTd style={{ textAlign: 'center' }}>
|
||||||
<Box>
|
<Box>
|
||||||
<Tooltip label="Lihat Detail" withArrow>
|
<Tooltip label="Lihat Detail" withArrow>
|
||||||
@@ -152,8 +198,9 @@ function Page() {
|
|||||||
color="blue"
|
color="blue"
|
||||||
leftSection={<IconDeviceImacCog size={16} />}
|
leftSection={<IconDeviceImacCog size={16} />}
|
||||||
onClick={() => router.push(`/darmasaba/ppid/daftar-informasi-publik/${item.id}`)}
|
onClick={() => router.push(`/darmasaba/ppid/daftar-informasi-publik/${item.id}`)}
|
||||||
|
aria-label={`Detail ${item.jenisInformasi}`}
|
||||||
>
|
>
|
||||||
Detail
|
<Text fz="xs" lh={1.2}>Detail</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -178,17 +225,27 @@ function Page() {
|
|||||||
|
|
||||||
<Paper p="lg" radius="xl" shadow="xs" withBorder>
|
<Paper p="lg" radius="xl" shadow="xs" withBorder>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Text fz="lg" fw="bold" c={colors["blue-button"]}>Kontak PPID</Text>
|
<Title order={5} fz={{ base: 'lg', md: 'xl' }} fw="bold" c={colors['blue-button']} lh={1.2}>
|
||||||
|
Kontak PPID
|
||||||
|
</Title>
|
||||||
|
|
||||||
<Group>
|
<Group>
|
||||||
<IconMail color='gray' size={16} style={{ marginRight: 6 }} />
|
<IconMail color="gray" size={16} style={{ marginRight: 6 }} />
|
||||||
<Text c={"dimmed"} fz="sm" lh={1.6}>
|
<Text c="dimmed" fz="sm" lh={1.6}>
|
||||||
Email: <Text c={"dimmed"} span fw="500">ppid@desadarmasaba.id</Text>
|
Email:{' '}
|
||||||
|
<Text c="dimmed" span fw={500} fz="sm" lh={1.6}>
|
||||||
|
ppid@desadarmasaba.id
|
||||||
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Group>
|
<Group>
|
||||||
<IconBrandWhatsapp color='gray' size={16} style={{ marginRight: 6 }} />
|
<IconBrandWhatsapp color="gray" size={16} style={{ marginRight: 6 }} />
|
||||||
<Text c={"dimmed"} fz="sm" lh={1.6}>
|
<Text c="dimmed" fz="sm" lh={1.6}>
|
||||||
WhatsApp: <Text c={"dimmed"} span fw="500">081-xxx-xxx-xxx</Text>
|
WhatsApp:{' '}
|
||||||
|
<Text c="dimmed" span fw={500} fz="sm" lh={1.6}>
|
||||||
|
081-xxx-xxx-xxx
|
||||||
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import stateDasarHukum from '@/app/admin/(dashboard)/_state/ppid/dasar_hukum/dasarHukum';
|
import stateDasarHukum from '@/app/admin/(dashboard)/_state/ppid/dasar_hukum/dasarHukum';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Paper, Skeleton, Stack, Text, Transition } from '@mantine/core';
|
import { Box, Paper, Skeleton, Stack, Text, Transition, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import { IconBook2 } from '@tabler/icons-react';
|
import { IconBook2 } from '@tabler/icons-react';
|
||||||
@@ -31,31 +31,39 @@ function Page() {
|
|||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
</Box>
|
||||||
<Stack
|
|
||||||
align="center"
|
{/* HEADER */}
|
||||||
gap="xs"
|
<Stack align="center" gap="xs" px={{ base: 'md', md: 100 }}>
|
||||||
px={{ base: 'md', md: 100 }}
|
|
||||||
>
|
|
||||||
<IconBook2 size={42} stroke={1.5} color={colors["blue-button"]} />
|
<IconBook2 size={42} stroke={1.5} color={colors["blue-button"]} />
|
||||||
<Text
|
|
||||||
|
<Title
|
||||||
|
order={1}
|
||||||
ta="center"
|
ta="center"
|
||||||
fz={{ base: "2rem", md: "2.5rem" }}
|
|
||||||
c={colors["blue-button"]}
|
c={colors["blue-button"]}
|
||||||
fw="bold"
|
fz={{ base: "1.8rem", md: "2.3rem" }}
|
||||||
|
lh={1.2}
|
||||||
style={{ letterSpacing: "-0.5px" }}
|
style={{ letterSpacing: "-0.5px" }}
|
||||||
>
|
>
|
||||||
Dasar Hukum
|
Dasar Hukum
|
||||||
</Text>
|
</Title>
|
||||||
<Text ta="center" fz="md" c={"black"}>
|
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: "sm", md: "md" }}
|
||||||
|
lh={1.6}
|
||||||
|
c="black"
|
||||||
|
>
|
||||||
Informasi regulasi dan kebijakan resmi yang menjadi dasar hukum
|
Informasi regulasi dan kebijakan resmi yang menjadi dasar hukum
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
{/* CONTENT */}
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
<Box px={{ base: "md", md: 100 }}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
{dataArray.map((item, k) => (
|
{dataArray.map((item, k) => (
|
||||||
<Transition
|
<Transition
|
||||||
key={k}
|
key={k}
|
||||||
mounted={true}
|
mounted
|
||||||
transition="fade-up"
|
transition="fade-up"
|
||||||
duration={400}
|
duration={400}
|
||||||
timingFunction="ease"
|
timingFunction="ease"
|
||||||
@@ -73,19 +81,27 @@ function Page() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Text
|
|
||||||
|
{/* JUDUL */}
|
||||||
|
<Title
|
||||||
|
order={3}
|
||||||
ta="center"
|
ta="center"
|
||||||
c={"black"}
|
c="black"
|
||||||
fw="bold"
|
fz={{ base: "lg", md: "xl" }}
|
||||||
fz={{ base: 'lg', md: 'xl' }}
|
lh={1.3}
|
||||||
style={{ lineHeight: 1.4 }}
|
|
||||||
dangerouslySetInnerHTML={{ __html: item.judul }}
|
dangerouslySetInnerHTML={{ __html: item.judul }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* CONTENT */}
|
||||||
<Text
|
<Text
|
||||||
c={"black"}
|
c="black"
|
||||||
ta={"justify"}
|
ta="justify"
|
||||||
fz={{ base: 'sm', md: 'md' }}
|
fz={{ base: "sm", md: "md" }}
|
||||||
style={{ lineHeight: 1.7, wordBreak: "break-word", whiteSpace: "normal" }}
|
lh={1.7}
|
||||||
|
style={{
|
||||||
|
wordBreak: "break-word",
|
||||||
|
whiteSpace: "normal",
|
||||||
|
}}
|
||||||
dangerouslySetInnerHTML={{ __html: item.content }}
|
dangerouslySetInnerHTML={{ __html: item.content }}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -3,7 +3,22 @@
|
|||||||
import indeksKepuasanState from "@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan";
|
import indeksKepuasanState from "@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan";
|
||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
import { BarChart, PieChart } from '@mantine/charts';
|
import { BarChart, PieChart } from '@mantine/charts';
|
||||||
import { Box, Button, Center, Container, Flex, Modal, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from "@mantine/core";
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Container,
|
||||||
|
Flex,
|
||||||
|
Modal,
|
||||||
|
Paper,
|
||||||
|
Select,
|
||||||
|
SimpleGrid,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Title
|
||||||
|
} from "@mantine/core";
|
||||||
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
|
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useProxy } from "valtio/utils";
|
import { useProxy } from "valtio/utils";
|
||||||
@@ -15,8 +30,6 @@ interface ChartDataItem {
|
|||||||
label?: string;
|
label?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function Kepuasan() {
|
function Kepuasan() {
|
||||||
const state = useProxy(indeksKepuasanState.responden);
|
const state = useProxy(indeksKepuasanState.responden);
|
||||||
const { data, loading } = state.findMany;
|
const { data, loading } = state.findMany;
|
||||||
@@ -24,7 +37,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
const [donutDataRating, setDonutDataRating] = useState<ChartDataItem[]>([]);
|
const [donutDataRating, setDonutDataRating] = useState<ChartDataItem[]>([]);
|
||||||
const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState<ChartDataItem[]>([]);
|
const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState<ChartDataItem[]>([]);
|
||||||
const [barChartData, setBarChartData] = useState<Array<{ month: string; Responden: number }>>([]);
|
const [barChartData, setBarChartData] = useState<Array<{ month: string; Responden: number }>>([]);
|
||||||
const [opened, { open, close }] = useDisclosure(false)
|
const [opened, { open, close }] = useDisclosure(false);
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
state.create.form = {
|
state.create.form = {
|
||||||
@@ -34,14 +47,14 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
jenisKelaminId: "",
|
jenisKelaminId: "",
|
||||||
ratingId: "",
|
ratingId: "",
|
||||||
kelompokUmurId: "",
|
kelompokUmurId: "",
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
indeksKepuasanState.jenisKelaminResponden.findMany.load()
|
indeksKepuasanState.jenisKelaminResponden.findMany.load();
|
||||||
indeksKepuasanState.pilihanRatingResponden.findMany.load()
|
indeksKepuasanState.pilihanRatingResponden.findMany.load();
|
||||||
indeksKepuasanState.kelompokUmurResponden.findMany.load()
|
indeksKepuasanState.kelompokUmurResponden.findMany.load();
|
||||||
},[])
|
}, []);
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -51,11 +64,11 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
await state.findUnique.load(idStr);
|
await state.findUnique.load(idStr);
|
||||||
}
|
}
|
||||||
resetForm();
|
resetForm();
|
||||||
close()
|
close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error submitting form:', error);
|
console.error('Error submitting form:', error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Load data on component mount
|
// Load data on component mount
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
@@ -154,33 +167,52 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Stack p="sm">
|
<Stack p="sm">
|
||||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"}>
|
<Container w={{ base: "100%", md: "80%" }} p="xl">
|
||||||
<Center>
|
<Center>
|
||||||
<Text fw={"bold"} fz={{ base: "1.8rem", md: "3.4rem" }}>Indeks Kepuasan Masyarakat</Text>
|
{/* Main page title — converted to Title, use order (don't set fz according to rules) */}
|
||||||
|
<Title order={2} ta="center" c="dark">
|
||||||
|
Indeks Kepuasan Masyarakat
|
||||||
|
</Title>
|
||||||
</Center>
|
</Center>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!</Text>
|
|
||||||
|
{/* Body lead text — responsive fz & lh */}
|
||||||
|
<Text ta="center" fz={{ base: "1rem", md: "1.25rem" }} lh={{ base: 1.4, md: 1.6 }} c="dimmed" mt="sm">
|
||||||
|
Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!
|
||||||
|
</Text>
|
||||||
|
|
||||||
<Center mt={10}>
|
<Center mt={10}>
|
||||||
<Button
|
<Button
|
||||||
radius={"lg"}
|
radius="lg"
|
||||||
onClick={open}
|
onClick={open}
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
gradient={{ from: "#26667F", to: "#124170" }}
|
gradient={{ from: "#26667F", to: "#124170" }}
|
||||||
>Ajukan Responden</Button>
|
>
|
||||||
|
Ajukan Responden
|
||||||
|
</Button>
|
||||||
</Center>
|
</Center>
|
||||||
</Container>
|
</Container>
|
||||||
<Box px={"xl"}>
|
|
||||||
<Paper p={"lg"} bg={colors.Bg}>
|
<Box px="xl">
|
||||||
<Paper p={"lg"}>
|
<Paper p="lg" bg={colors.Bg}>
|
||||||
<Stack gap={"xs"}>
|
<Paper p="lg">
|
||||||
<Flex justify={"space-between"} align={"center"}>
|
<Stack gap="xs">
|
||||||
<Text fw={"bold"}>Pelayanan Terhadap Publik Desa Darmasaba</Text>
|
<Flex justify="space-between" align="center">
|
||||||
|
{/* Section heading — use Title order for hierarchy */}
|
||||||
|
<Title order={4}>
|
||||||
|
Pelayanan Terhadap Publik Desa Darmasaba
|
||||||
|
</Title>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz={"sm"} fw={"bold"} c={colors["blue-button"]}>Total Responden</Text>
|
<Text fz={{ base: "0.9rem", md: "1rem" }} fw="bold" c={colors["blue-button"]}>
|
||||||
<Text ta={"end"} fz={"h1"} fw={"bold"} c={colors["blue-button"]}>
|
Total Responden
|
||||||
{state.findMany.total.toLocaleString('id-ID')}
|
|
||||||
</Text>
|
</Text>
|
||||||
|
{/* Big number — use Title for emphasis */}
|
||||||
|
<Title order={3} ta="end" c={colors["blue-button"]} fw="bold" mt="xs">
|
||||||
|
{state.findMany.total.toLocaleString('id-ID')}
|
||||||
|
</Title>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<BarChart
|
<BarChart
|
||||||
h={window.innerWidth < 480 ? 200 : 300}
|
h={window.innerWidth < 480 ? 200 : 300}
|
||||||
data={barChartData}
|
data={barChartData}
|
||||||
@@ -194,18 +226,16 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Box py={"xl"}>
|
|
||||||
<SimpleGrid
|
<Box py="xl">
|
||||||
cols={{ base: 1, sm: 2, lg: 3 }}
|
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md" verticalSpacing="md">
|
||||||
spacing="md"
|
|
||||||
verticalSpacing="md"
|
|
||||||
>
|
|
||||||
{/* Chart Jenis Kelamin */}
|
{/* Chart Jenis Kelamin */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Jenis Kelamin</Title>
|
<Title order={5}>Jenis Kelamin</Title>
|
||||||
|
|
||||||
{donutDataJenisKelamin.every(item => item.value === 0) ? (
|
{donutDataJenisKelamin.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz={{ base: "0.95rem", md: "1rem" }}>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
@@ -218,7 +248,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
withTooltip
|
withTooltip
|
||||||
labelsPosition="inside"
|
labelsPosition="inside"
|
||||||
labelsType="percent"
|
labelsType="percent"
|
||||||
size={250} // Fixed size in pixels
|
size={250}
|
||||||
data={donutDataJenisKelamin}
|
data={donutDataJenisKelamin}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
@@ -227,7 +257,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{donutDataJenisKelamin.map((entry) => (
|
{donutDataJenisKelamin.map((entry) => (
|
||||||
<Flex key={entry.name} gap="md" align="center">
|
<Flex key={entry.name} gap="md" align="center">
|
||||||
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
||||||
<Text size="sm">{entry.name}: {entry.value}</Text>
|
<Text fz={{ base: "0.95rem", md: "1rem" }}>{entry.name}: {entry.value}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -240,9 +270,10 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{/* Chart Rating */}
|
{/* Chart Rating */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Ulasan</Title>
|
<Title order={5}>Ulasan</Title>
|
||||||
|
|
||||||
{donutDataRating.every(item => item.value === 0) ? (
|
{donutDataRating.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz={{ base: "0.95rem", md: "1rem" }}>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
@@ -267,7 +298,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{donutDataRating.map((entry) => (
|
{donutDataRating.map((entry) => (
|
||||||
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
||||||
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
||||||
<Text size="xs" lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
<Text fz={{ base: "0.85rem", md: "0.95rem" }} lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
{entry.name}: {entry.value}
|
{entry.name}: {entry.value}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -283,9 +314,10 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{/* Chart Kelompok Umur */}
|
{/* Chart Kelompok Umur */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Umur</Title>
|
<Title order={5}>Umur</Title>
|
||||||
|
|
||||||
{donutDataKelompokUmur.every(item => item.value === 0) ? (
|
{donutDataKelompokUmur.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz={{ base: "0.95rem", md: "1rem" }}>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
@@ -310,7 +342,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{donutDataKelompokUmur.map((entry) => (
|
{donutDataKelompokUmur.map((entry) => (
|
||||||
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
||||||
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
||||||
<Text size="xs" lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
<Text fz={{ base: "0.85rem", md: "0.95rem" }} lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
{entry.name}: {entry.value}
|
{entry.name}: {entry.value}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -326,18 +358,21 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
<Modal opened={opened} onClose={close} title="Ajukan Responden" centered>
|
<Modal opened={opened} onClose={close} title="Ajukan Responden" centered>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors['white-1']} p="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Nama"
|
label="Nama"
|
||||||
type='text'
|
type="text"
|
||||||
placeholder="Masukkan nama"
|
placeholder="Masukkan nama"
|
||||||
value={state.create.form.name}
|
value={state.create.form.name}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
state.create.form.name = val.currentTarget.value;
|
state.create.form.name = val.currentTarget.value;
|
||||||
}}
|
}}
|
||||||
|
// label typography
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Tanggal"
|
label="Tanggal"
|
||||||
@@ -347,10 +382,11 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
state.create.form.tanggal = val.currentTarget.value;
|
state.create.form.tanggal = val.currentTarget.value;
|
||||||
}}
|
}}
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
key={"jenisKelamin"}
|
key="jenisKelamin"
|
||||||
label={"Jenis Kelamin"}
|
label="Jenis Kelamin"
|
||||||
placeholder={indeksKepuasanState.jenisKelaminResponden.findMany.loading ? 'Memuat...' : 'Pilih jenis kelamin'}
|
placeholder={indeksKepuasanState.jenisKelaminResponden.findMany.loading ? 'Memuat...' : 'Pilih jenis kelamin'}
|
||||||
value={state.create.form.jenisKelaminId || ""}
|
value={state.create.form.jenisKelaminId || ""}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
@@ -358,17 +394,19 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
}}
|
}}
|
||||||
data={
|
data={
|
||||||
(indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
|
(indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
|
||||||
.filter(Boolean) // Hapus null, undefined, dll
|
.filter(Boolean)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.name || 'Tanpa Nama',
|
label: item.name || 'Tanpa Nama',
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
disabled={indeksKepuasanState.jenisKelaminResponden.findMany.loading}
|
disabled={indeksKepuasanState.jenisKelaminResponden.findMany.loading}
|
||||||
|
// label typography
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
key={"rating_responden"}
|
key="rating_responden"
|
||||||
label={"Rating"}
|
label="Rating"
|
||||||
placeholder={indeksKepuasanState.pilihanRatingResponden.findMany.loading ? 'Memuat...' : 'Pilih rating'}
|
placeholder={indeksKepuasanState.pilihanRatingResponden.findMany.loading ? 'Memuat...' : 'Pilih rating'}
|
||||||
value={state.create.form.ratingId || ""}
|
value={state.create.form.ratingId || ""}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
@@ -376,17 +414,18 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
}}
|
}}
|
||||||
data={
|
data={
|
||||||
(indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
|
(indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
|
||||||
.filter(Boolean) // Hapus null, undefined, dll
|
.filter(Boolean)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.name || 'Tanpa Nama',
|
label: item.name || 'Tanpa Nama',
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
disabled={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
|
disabled={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
key={"kelompokUmur"}
|
key="kelompokUmur"
|
||||||
label={"Kelompok Umur"}
|
label="Kelompok Umur"
|
||||||
placeholder={indeksKepuasanState.kelompokUmurResponden.findMany.loading ? 'Memuat...' : 'Pilih kelompok umur'}
|
placeholder={indeksKepuasanState.kelompokUmurResponden.findMany.loading ? 'Memuat...' : 'Pilih kelompok umur'}
|
||||||
value={state.create.form.kelompokUmurId || ""}
|
value={state.create.form.kelompokUmurId || ""}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
@@ -394,19 +433,16 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
}}
|
}}
|
||||||
data={
|
data={
|
||||||
(indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
|
(indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
|
||||||
.filter(Boolean) // Hapus null, undefined, dll
|
.filter(Boolean)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.name || 'Tanpa Nama',
|
label: item.name || 'Tanpa Nama',
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
disabled={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
|
disabled={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button mt={10} bg={colors['blue-button']} onClick={handleSubmit}>
|
||||||
mt={10}
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
onClick={handleSubmit}
|
|
||||||
>
|
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -415,36 +451,47 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p={"sm"}>
|
<Stack p="sm">
|
||||||
<Container size="lg" px="md">
|
<Container size="lg" px="md">
|
||||||
<Center>
|
<Center>
|
||||||
<Text ta={"center"} fz={{ base: "2.4rem", md: "3.4rem" }}>Indeks Kepuasan Masyarakat</Text>
|
{/* Main page title — Title with order */}
|
||||||
|
<Title order={2} ta="center" c="dark">
|
||||||
|
Indeks Kepuasan Masyarakat
|
||||||
|
</Title>
|
||||||
</Center>
|
</Center>
|
||||||
<Text fz={{ base: "1.2rem", md: "1.4rem" }} ta={"center"}>Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!</Text>
|
|
||||||
|
<Text fz={{ base: "1rem", md: "1.125rem" }} lh={{ base: 1.4, md: 1.6 }} ta="center" c="dimmed" mt="sm">
|
||||||
|
Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!
|
||||||
|
</Text>
|
||||||
|
|
||||||
<Center mt={10}>
|
<Center mt={10}>
|
||||||
<Button radius={"lg"} bg={colors["blue-button"]} onClick={open}>Ajukan Responden</Button>
|
<Button radius="lg" bg={colors["blue-button"]} onClick={open}>Ajukan Responden</Button>
|
||||||
</Center>
|
</Center>
|
||||||
</Container>
|
</Container>
|
||||||
<Box px={"xl"}>
|
|
||||||
<Paper p={"lg"} bg={colors.Bg}>
|
<Box px="xl">
|
||||||
<Paper p={"lg"}>
|
<Paper p="lg" bg={colors.Bg}>
|
||||||
<Stack gap={"xs"}>
|
<Paper p="lg">
|
||||||
|
<Stack gap="xs">
|
||||||
<Flex
|
<Flex
|
||||||
direction={{ base: "column", sm: "row" }}
|
direction={{ base: "column", sm: "row" }}
|
||||||
justify="space-between"
|
justify="space-between"
|
||||||
align={{ base: "flex-start", sm: "center" }}
|
align={{ base: "flex-start", sm: "center" }}
|
||||||
>
|
>
|
||||||
<Text fw="bold" ta={{ base: "center", sm: "left" }}>
|
<Title order={4} ta={{ base: "center", sm: "left" }}>
|
||||||
Pelayanan Terhadap Publik Desa Darmasaba
|
Pelayanan Terhadap Publik Desa Darmasaba
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
<Box mt={{ base: "sm", sm: 0 }}>
|
<Box mt={{ base: "sm", sm: 0 }}>
|
||||||
<Text fz={"sm"} fw={"bold"} c={colors["blue-button"]}>Total Responden</Text>
|
<Text fz={{ base: "0.9rem", md: "1rem" }} fw="bold" c={colors["blue-button"]}>Total Responden</Text>
|
||||||
<Text ta={"end"} fz={"h1"} fw={"bold"} c={colors["blue-button"]}>
|
<Title order={3} ta="end" c={colors["blue-button"]} fw="bold" mt="xs">
|
||||||
{state.findMany.total.toLocaleString('id-ID')}
|
{state.findMany.total.toLocaleString('id-ID')}
|
||||||
</Text>
|
</Title>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<BarChart
|
<BarChart
|
||||||
h={300}
|
h={300}
|
||||||
data={barChartData}
|
data={barChartData}
|
||||||
@@ -458,21 +505,18 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Box py={"xl"}>
|
|
||||||
|
<Box py="xl">
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
cols={{
|
cols={{ base: 1, md: 1, lg: 1, xl: 3 }}
|
||||||
base: 1,
|
|
||||||
md: 1,
|
|
||||||
lg: 1,
|
|
||||||
xl: 3
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{/* Chart Jenis Kelamin */}
|
{/* Chart Jenis Kelamin */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Jenis Kelamin</Title>
|
<Title order={5}>Jenis Kelamin</Title>
|
||||||
|
|
||||||
{donutDataJenisKelamin.every(item => item.value === 0) ? (
|
{donutDataJenisKelamin.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz={{ base: "0.95rem", md: "1rem" }}>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
@@ -494,7 +538,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{donutDataJenisKelamin.map((entry) => (
|
{donutDataJenisKelamin.map((entry) => (
|
||||||
<Flex key={entry.name} gap="md" align="center">
|
<Flex key={entry.name} gap="md" align="center">
|
||||||
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
||||||
<Text size="sm">{entry.name}: {entry.value}</Text>
|
<Text fz={{ base: "0.95rem", md: "1rem" }}>{entry.name}: {entry.value}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -507,9 +551,10 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{/* Chart Rating */}
|
{/* Chart Rating */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Ulasan</Title>
|
<Title order={5}>Ulasan</Title>
|
||||||
|
|
||||||
{donutDataRating.every(item => item.value === 0) ? (
|
{donutDataRating.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz={{ base: "0.95rem", md: "1rem" }}>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
@@ -534,7 +579,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{donutDataRating.map((entry) => (
|
{donutDataRating.map((entry) => (
|
||||||
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
||||||
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
||||||
<Text size="xs" lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
<Text fz={{ base: "0.85rem", md: "0.95rem" }} lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
{entry.name}: {entry.value}
|
{entry.name}: {entry.value}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -550,9 +595,10 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{/* Chart Kelompok Umur */}
|
{/* Chart Kelompok Umur */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Umur</Title>
|
<Title order={5}>Umur</Title>
|
||||||
|
|
||||||
{donutDataKelompokUmur.every(item => item.value === 0) ? (
|
{donutDataKelompokUmur.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz={{ base: "0.95rem", md: "1rem" }}>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
@@ -577,7 +623,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{donutDataKelompokUmur.map((entry) => (
|
{donutDataKelompokUmur.map((entry) => (
|
||||||
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
||||||
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
||||||
<Text size="xs" lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
<Text fz={{ base: "0.85rem", md: "0.95rem" }} lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
{entry.name}: {entry.value}
|
{entry.name}: {entry.value}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -593,18 +639,20 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
<Modal opened={opened} onClose={close} title="Ajukan Responden" centered>
|
<Modal opened={opened} onClose={close} title="Ajukan Responden" centered>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors['white-1']} p="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Nama"
|
label="Nama"
|
||||||
type='text'
|
type="text"
|
||||||
placeholder="Masukkan nama"
|
placeholder="Masukkan nama"
|
||||||
value={state.create.form.name}
|
value={state.create.form.name}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
state.create.form.name = val.currentTarget.value;
|
state.create.form.name = val.currentTarget.value;
|
||||||
}}
|
}}
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Tanggal Pengisian"
|
label="Tanggal Pengisian"
|
||||||
@@ -614,10 +662,11 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
state.create.form.tanggal = val.currentTarget.value;
|
state.create.form.tanggal = val.currentTarget.value;
|
||||||
}}
|
}}
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
key={"jenisKelamin"}
|
key="jenisKelamin"
|
||||||
label={"Jenis Kelamin"}
|
label="Jenis Kelamin"
|
||||||
placeholder={indeksKepuasanState.jenisKelaminResponden.findMany.loading ? 'Memuat...' : 'Pilih jenis kelamin'}
|
placeholder={indeksKepuasanState.jenisKelaminResponden.findMany.loading ? 'Memuat...' : 'Pilih jenis kelamin'}
|
||||||
value={state.create.form.jenisKelaminId || ""}
|
value={state.create.form.jenisKelaminId || ""}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
@@ -625,17 +674,18 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
}}
|
}}
|
||||||
data={
|
data={
|
||||||
(indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
|
(indeksKepuasanState.jenisKelaminResponden.findMany.data || [])
|
||||||
.filter(Boolean) // Hapus null, undefined, dll
|
.filter(Boolean)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.name || 'Tanpa Nama',
|
label: item.name || 'Tanpa Nama',
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
disabled={indeksKepuasanState.jenisKelaminResponden.findMany.loading}
|
disabled={indeksKepuasanState.jenisKelaminResponden.findMany.loading}
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
key={"rating_responden"}
|
key="rating_responden"
|
||||||
label={"Rating"}
|
label="Rating"
|
||||||
placeholder={indeksKepuasanState.pilihanRatingResponden.findMany.loading ? 'Memuat...' : 'Pilih rating'}
|
placeholder={indeksKepuasanState.pilihanRatingResponden.findMany.loading ? 'Memuat...' : 'Pilih rating'}
|
||||||
value={state.create.form.ratingId || ""}
|
value={state.create.form.ratingId || ""}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
@@ -643,17 +693,18 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
}}
|
}}
|
||||||
data={
|
data={
|
||||||
(indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
|
(indeksKepuasanState.pilihanRatingResponden.findMany.data || [])
|
||||||
.filter(Boolean) // Hapus null, undefined, dll
|
.filter(Boolean)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.name || 'Tanpa Nama',
|
label: item.name || 'Tanpa Nama',
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
disabled={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
|
disabled={indeksKepuasanState.pilihanRatingResponden.findMany.loading}
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
key={"kelompokUmur"}
|
key="kelompokUmur"
|
||||||
label={"Kelompok Umur"}
|
label="Kelompok Umur"
|
||||||
placeholder={indeksKepuasanState.kelompokUmurResponden.findMany.loading ? 'Memuat...' : 'Pilih kelompok umur'}
|
placeholder={indeksKepuasanState.kelompokUmurResponden.findMany.loading ? 'Memuat...' : 'Pilih kelompok umur'}
|
||||||
value={state.create.form.kelompokUmurId || ""}
|
value={state.create.form.kelompokUmurId || ""}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
@@ -661,19 +712,16 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
}}
|
}}
|
||||||
data={
|
data={
|
||||||
(indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
|
(indeksKepuasanState.kelompokUmurResponden.findMany.data || [])
|
||||||
.filter(Boolean) // Hapus null, undefined, dll
|
.filter(Boolean)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.name || 'Tanpa Nama',
|
label: item.name || 'Tanpa Nama',
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
disabled={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
|
disabled={indeksKepuasanState.kelompokUmurResponden.findMany.loading}
|
||||||
|
labelProps={{ style: { fontSize: '0.95rem', lineHeight: '1.4' } } as any}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button mt={10} bg={colors['blue-button']} onClick={handleSubmit}>
|
||||||
mt={10}
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
onClick={handleSubmit}
|
|
||||||
>
|
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import {
|
|||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput
|
TextInput,
|
||||||
|
Title
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { IconSend2 } from '@tabler/icons-react';
|
import { IconSend2 } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -55,7 +56,7 @@ function Page() {
|
|||||||
|
|
||||||
const submitForms = async () => {
|
const submitForms = async () => {
|
||||||
const { create } = permohonanInformasiPublikState.statepermohonanInformasiPublik;
|
const { create } = permohonanInformasiPublikState.statepermohonanInformasiPublik;
|
||||||
const hasil = await create.create(); // tunggu hasilnya
|
const hasil = await create.create();
|
||||||
if (hasil) {
|
if (hasil) {
|
||||||
router.push('/darmasaba/permohonan/berhasil');
|
router.push('/darmasaba/permohonan/berhasil');
|
||||||
}
|
}
|
||||||
@@ -67,14 +68,17 @@ function Page() {
|
|||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Text
|
{/* MAIN PAGE TITLE */}
|
||||||
|
<Title
|
||||||
|
order={1}
|
||||||
ta="center"
|
ta="center"
|
||||||
fz={{ base: '2rem', md: '2.5rem' }}
|
fz={{ base: '1.8rem', sm: '2.2rem', md: '2.6rem' }}
|
||||||
|
lh={1.2}
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
fw="bold"
|
style={{ fontWeight: 700 }}
|
||||||
>
|
>
|
||||||
Permohonan Informasi Publik
|
Permohonan Informasi Publik
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
@@ -85,15 +89,18 @@ function Page() {
|
|||||||
shadow="sm"
|
shadow="sm"
|
||||||
bg={colors['white-trans-1']}
|
bg={colors['white-trans-1']}
|
||||||
>
|
>
|
||||||
<Text
|
{/* SUBTITLE */}
|
||||||
|
<Title
|
||||||
|
order={2}
|
||||||
pb={30}
|
pb={30}
|
||||||
ta="center"
|
ta="center"
|
||||||
fw="bold"
|
fz={{ base: '1.4rem', md: '1.8rem' }}
|
||||||
fz={{ base: 'h4', md: 'h3' }}
|
lh={1.3}
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
|
style={{ fontWeight: 700 }}
|
||||||
>
|
>
|
||||||
Tata Cara Permohonan
|
Tata Cara Permohonan
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
<SimpleGrid pb={30} cols={{ base: 1, sm: 2, lg: 4 }} spacing="lg">
|
<SimpleGrid pb={30} cols={{ base: 1, sm: 2, lg: 4 }} spacing="lg">
|
||||||
{steps.map((v) => (
|
{steps.map((v) => (
|
||||||
@@ -116,27 +123,38 @@ function Page() {
|
|||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
ta="center"
|
ta="center"
|
||||||
fw="bold"
|
fw="bold"
|
||||||
fz="h3"
|
fz="h2"
|
||||||
|
lh={1}
|
||||||
>
|
>
|
||||||
{v.number}
|
{v.number}
|
||||||
</Text>
|
</Text>
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Center>
|
</Center>
|
||||||
|
|
||||||
|
<Title
|
||||||
|
order={4}
|
||||||
|
ta="center"
|
||||||
|
c={colors['white-1']}
|
||||||
|
fz="lg"
|
||||||
|
lh={1.3}
|
||||||
|
style={{ fontWeight: 700 }}
|
||||||
|
>
|
||||||
|
{v.title}
|
||||||
|
</Title>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
ta="center"
|
ta="center"
|
||||||
c={colors['white-1']}
|
c={colors['white-1']}
|
||||||
fw="bold"
|
fz="sm"
|
||||||
fz="lg"
|
lh={1.4}
|
||||||
>
|
>
|
||||||
{v.title}
|
|
||||||
</Text>
|
|
||||||
<Text ta="center" c={colors['white-1']} fz="sm">
|
|
||||||
{v.desc}
|
{v.desc}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
|
||||||
<Group justify="center">
|
<Group justify="center">
|
||||||
<Paper
|
<Paper
|
||||||
p="xl"
|
p="xl"
|
||||||
@@ -148,15 +166,20 @@ function Page() {
|
|||||||
maw={800}
|
maw={800}
|
||||||
>
|
>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Text
|
|
||||||
fw="bold"
|
{/* FORM TITLE */}
|
||||||
fz={{ base: 'h4', md: 'h3' }}
|
<Title
|
||||||
|
order={2}
|
||||||
ta="center"
|
ta="center"
|
||||||
|
fz={{ base: '1.4rem', md: '1.8rem' }}
|
||||||
|
lh={1.3}
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
|
style={{ fontWeight: 700 }}
|
||||||
>
|
>
|
||||||
Formulir Permohonan Informasi
|
Formulir Permohonan Informasi
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
|
{/* INPUTS */}
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Nama Lengkap"
|
label="Nama Lengkap"
|
||||||
placeholder="Masukkan nama lengkap Anda"
|
placeholder="Masukkan nama lengkap Anda"
|
||||||
@@ -166,6 +189,7 @@ function Page() {
|
|||||||
val.target.value;
|
val.target.value;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Nomor Induk Kependudukan (NIK)"
|
label="Nomor Induk Kependudukan (NIK)"
|
||||||
placeholder="Masukkan NIK"
|
placeholder="Masukkan NIK"
|
||||||
@@ -175,6 +199,7 @@ function Page() {
|
|||||||
val.target.value;
|
val.target.value;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Nomor Telepon"
|
label="Nomor Telepon"
|
||||||
placeholder="Masukkan nomor telepon aktif"
|
placeholder="Masukkan nomor telepon aktif"
|
||||||
@@ -184,6 +209,7 @@ function Page() {
|
|||||||
val.target.value;
|
val.target.value;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Alamat Lengkap"
|
label="Alamat Lengkap"
|
||||||
placeholder="Masukkan alamat sesuai identitas"
|
placeholder="Masukkan alamat sesuai identitas"
|
||||||
@@ -193,6 +219,7 @@ function Page() {
|
|||||||
val.target.value;
|
val.target.value;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Alamat Email"
|
label="Alamat Email"
|
||||||
placeholder="Masukkan alamat email aktif"
|
placeholder="Masukkan alamat email aktif"
|
||||||
@@ -209,12 +236,14 @@ function Page() {
|
|||||||
val.id;
|
val.id;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MemperolehInformasi
|
<MemperolehInformasi
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.caraMemperolehInformasiId =
|
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.caraMemperolehInformasiId =
|
||||||
val.id;
|
val.id;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MemperolehSalinan
|
<MemperolehSalinan
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.caraMemperolehSalinanInformasiId =
|
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.caraMemperolehSalinanInformasiId =
|
||||||
@@ -241,6 +270,7 @@ function Page() {
|
|||||||
Kirim Permohonan
|
Kirim Permohonan
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
|
Title,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
@@ -56,13 +57,8 @@ function Page() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
const { create } = stateKeberatan;
|
const hasil = await stateKeberatan.create.create();
|
||||||
|
if (hasil) router.push('/darmasaba/permohonan/berhasil');
|
||||||
const hasil = await create.create(); // tunggu hasilnya
|
|
||||||
|
|
||||||
if (hasil) {
|
|
||||||
router.push('/darmasaba/permohonan/berhasil');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -72,15 +68,16 @@ function Page() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Stack align="center" px={{ base: 'md', md: 100 }}>
|
<Stack align="center" px={{ base: 'md', md: 100 }}>
|
||||||
<Text
|
<Title
|
||||||
|
order={1}
|
||||||
ta="center"
|
ta="center"
|
||||||
fz={{ base: '2rem', md: '2.8rem' }}
|
fz={{ base: '1.8rem', md: '2.6rem' }}
|
||||||
|
lh={1.2}
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
fw={800}
|
style={{ letterSpacing: -0.5 }}
|
||||||
style={{ letterSpacing: '-0.5px' }}
|
|
||||||
>
|
>
|
||||||
Permohonan Keberatan Informasi Publik
|
Permohonan Keberatan Informasi Publik
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
<Paper
|
<Paper
|
||||||
p="xl"
|
p="xl"
|
||||||
@@ -90,26 +87,36 @@ function Page() {
|
|||||||
withBorder
|
withBorder
|
||||||
>
|
>
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
|
{/* Tentang */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={700} fz={{ base: 'lg', md: 'xl' }} mb={8}>
|
<Title order={3} fz={{ base: 'lg', md: 'xl' }} lh={1.3} mb={8}>
|
||||||
Tentang Permohonan Keberatan
|
Tentang Permohonan Keberatan
|
||||||
</Text>
|
</Title>
|
||||||
<Text ta="justify" fz={{ base: 'sm', md: 'md' }} lh={1.6}>
|
|
||||||
|
<Text
|
||||||
|
ta="justify"
|
||||||
|
fz={{ base: 'sm', md: 'md' }}
|
||||||
|
lh={1.7}
|
||||||
|
c="black"
|
||||||
|
>
|
||||||
Jika Anda merasa permohonan informasi tidak ditanggapi dengan
|
Jika Anda merasa permohonan informasi tidak ditanggapi dengan
|
||||||
baik atau ditolak, Anda berhak mengajukan keberatan melalui
|
baik atau ditolak, Anda berhak mengajukan keberatan melalui
|
||||||
formulir berikut.
|
formulir berikut.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Alur */}
|
||||||
<Stack>
|
<Stack>
|
||||||
<Text
|
<Title
|
||||||
|
order={3}
|
||||||
ta="center"
|
ta="center"
|
||||||
fw={700}
|
fw={700}
|
||||||
fz={{ base: 'xl', md: '2xl' }}
|
fz={{ base: 'xl', md: '2xl' }}
|
||||||
style={{ letterSpacing: '-0.5px' }}
|
lh={1.2}
|
||||||
|
style={{ letterSpacing: -0.5 }}
|
||||||
>
|
>
|
||||||
Alur Pengajuan Keberatan
|
Alur Pengajuan Keberatan
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
<SimpleGrid cols={{ base: 1, md: 4 }} spacing="lg">
|
<SimpleGrid cols={{ base: 1, md: 4 }} spacing="lg">
|
||||||
{data.map((v) => (
|
{data.map((v) => (
|
||||||
@@ -124,15 +131,23 @@ function Page() {
|
|||||||
<Center>
|
<Center>
|
||||||
<v.icon size={48} color={colors['white-1']} />
|
<v.icon size={48} color={colors['white-1']} />
|
||||||
</Center>
|
</Center>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
ta="center"
|
ta="center"
|
||||||
c={colors['white-1']}
|
c={colors['white-1']}
|
||||||
fw={700}
|
fw={700}
|
||||||
fz="lg"
|
fz="lg"
|
||||||
|
lh={1.3}
|
||||||
>
|
>
|
||||||
{v.title}
|
{v.title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text ta="center" c={colors['white-1']} fz="sm">
|
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
c={colors['white-1']}
|
||||||
|
fz="sm"
|
||||||
|
lh={1.6}
|
||||||
|
>
|
||||||
{v.desc}
|
{v.desc}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -141,6 +156,7 @@ function Page() {
|
|||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
<Group justify="center">
|
<Group justify="center">
|
||||||
<Paper
|
<Paper
|
||||||
p="xl"
|
p="xl"
|
||||||
@@ -152,14 +168,16 @@ function Page() {
|
|||||||
w="100%"
|
w="100%"
|
||||||
>
|
>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Text
|
<Title
|
||||||
|
order={3}
|
||||||
|
ta="center"
|
||||||
fw={700}
|
fw={700}
|
||||||
fz={{ base: 'lg', md: 'xl' }}
|
fz={{ base: 'lg', md: 'xl' }}
|
||||||
ta="center"
|
lh={1.3}
|
||||||
mb={4}
|
mb={4}
|
||||||
>
|
>
|
||||||
Formulir Keberatan
|
Formulir Keberatan
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Nama Lengkap"
|
label="Nama Lengkap"
|
||||||
@@ -196,7 +214,7 @@ function Page() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={600} fz="sm" mb={6}>
|
<Text fw={600} fz="sm" lh={1.4} mb={6}>
|
||||||
Alasan Keberatan
|
Alasan Keberatan
|
||||||
</Text>
|
</Text>
|
||||||
<PPIDTextEditor
|
<PPIDTextEditor
|
||||||
@@ -222,11 +240,13 @@ function Page() {
|
|||||||
</Paper>
|
</Paper>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
{/* Kontak */}
|
||||||
<Stack gap={4} pt="lg" align="center">
|
<Stack gap={4} pt="lg" align="center">
|
||||||
<Text fw={700} fz="lg">
|
<Title order={4} fw={700} fz="lg" lh={1.3}>
|
||||||
Kontak PPID
|
Kontak PPID
|
||||||
</Text>
|
</Title>
|
||||||
<Text fz="sm" c="dimmed">
|
|
||||||
|
<Text fz="sm" lh={1.5} c="dimmed" ta="center">
|
||||||
Email: desadarmasaba@badungkab.go.id | WhatsApp: 081-xxx-xxx-xxx
|
Email: desadarmasaba@badungkab.go.id | WhatsApp: 081-xxx-xxx-xxx
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
257
src/app/darmasaba/(pages)/ppid/profil-ppid/page.tsx
Normal file
257
src/app/darmasaba/(pages)/ppid/profil-ppid/page.tsx
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
'use client'
|
||||||
|
import stateProfilePPID from '@/app/admin/(dashboard)/_state/ppid/profile_ppid/profile_PPID';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Center,
|
||||||
|
Divider,
|
||||||
|
Flex,
|
||||||
|
Image,
|
||||||
|
List,
|
||||||
|
Paper,
|
||||||
|
SimpleGrid,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import {
|
||||||
|
IconBuildingCommunity,
|
||||||
|
IconTargetArrow,
|
||||||
|
IconTimeline,
|
||||||
|
IconUser,
|
||||||
|
} from '@tabler/icons-react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
|
import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton';
|
||||||
|
|
||||||
|
function Page() {
|
||||||
|
const allList = useProxy(stateProfilePPID);
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
allList.profile.load('edit');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// LOADING SKELETON
|
||||||
|
if (!allList.profile.data)
|
||||||
|
return (
|
||||||
|
<Stack bg={colors.Bg} py="xl" gap="22">
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Skeleton h={40} />
|
||||||
|
</Box>
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Skeleton h={80} />
|
||||||
|
</Box>
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Paper p="xl" bg={colors['white-trans-1']}>
|
||||||
|
{Array.from({ length: 8 }).map((_, i) => (
|
||||||
|
<Skeleton key={i} h={40} mb="sm" />
|
||||||
|
))}
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
|
||||||
|
const dataArray = Array.isArray(allList.profile.data)
|
||||||
|
? allList.profile.data
|
||||||
|
: [allList.profile.data];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
|
||||||
|
{/* Back Button */}
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<BackButton />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Page Title */}
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Title
|
||||||
|
order={1}
|
||||||
|
ta="center"
|
||||||
|
c={colors['blue-button']}
|
||||||
|
fz={{ base: '2rem', md: '2.7rem', lg: '3.2rem', xl: '3.6rem' }}
|
||||||
|
lh={{ base: 1.1, md: 1.1 }}
|
||||||
|
fw={900}
|
||||||
|
>
|
||||||
|
Profil PPID Desa Darmasaba
|
||||||
|
</Title>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{dataArray.map((item) => (
|
||||||
|
<Box key={item.id} px={{ base: 'md', md: 100 }}>
|
||||||
|
<Paper p="xl" bg={colors['white-trans-1']} radius="lg" shadow="xl">
|
||||||
|
{/* LOGO & TITLE */}
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Center>
|
||||||
|
<Image
|
||||||
|
loading="lazy"
|
||||||
|
src="/darmasaba-icon.png"
|
||||||
|
h={{ base: 70, md: 120 }}
|
||||||
|
w={{ base: 70, md: 120 }}
|
||||||
|
alt="Logo Desa"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
|
||||||
|
<Title
|
||||||
|
order={2}
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: '1.4rem', md: '2.2rem', lg: '2.6rem', xl: '3rem' }}
|
||||||
|
lh={1.1}
|
||||||
|
fw={800}
|
||||||
|
mt="md"
|
||||||
|
>
|
||||||
|
Pejabat Pengelola Informasi dan Dokumentasi
|
||||||
|
</Title>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Divider my="lg" />
|
||||||
|
|
||||||
|
{/* GRID BLOCK */}
|
||||||
|
<Box px={{ base: 0, md: 50 }} pb={40}>
|
||||||
|
<SimpleGrid cols={{ base: 1, xl: 2 }} spacing="xl">
|
||||||
|
{/* FOTO + NAMA */}
|
||||||
|
<Box px={{ base: 0, md: 50 }}>
|
||||||
|
<Paper bg={colors['white-trans-1']} radius="xl" shadow="md" withBorder>
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Image
|
||||||
|
pt={{ base: 0, md: 100 }}
|
||||||
|
px="lg"
|
||||||
|
src={
|
||||||
|
item.image?.link
|
||||||
|
? `${item.image.link}?t=${Date.now()}`
|
||||||
|
: '/perbekel.png'
|
||||||
|
}
|
||||||
|
alt="Foto Pimpinan"
|
||||||
|
radius="lg"
|
||||||
|
onError={(e) => (e.currentTarget.src = '/perbekel.png')}
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Paper
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
px="lg"
|
||||||
|
radius="0 0 var(--mantine-radius-xl) var(--mantine-radius-xl)"
|
||||||
|
className="glass3"
|
||||||
|
py={{ base: 20, md: 50 }}
|
||||||
|
>
|
||||||
|
<Title
|
||||||
|
order={3}
|
||||||
|
ta="center"
|
||||||
|
c={colors['white-1']}
|
||||||
|
fz={{ base: '1.4rem', md: '2.2rem' }}
|
||||||
|
lh={1.1}
|
||||||
|
fw={900}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Title>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* BIOGRAFI & RIWAYAT */}
|
||||||
|
<Box>
|
||||||
|
<Stack gap="xl">
|
||||||
|
{/* BIO */}
|
||||||
|
<Box>
|
||||||
|
<Flex align="center" gap="sm" mb="sm">
|
||||||
|
<IconUser size={28} />
|
||||||
|
<Title order={3} fz={{ base: '1.3rem', md: '1.6rem' }} lh={1.2} fw={800}>
|
||||||
|
Biografi
|
||||||
|
</Title>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Box px={20}>
|
||||||
|
<Text
|
||||||
|
fz={{ base: '1rem', md: '1.1rem', lg: '1.2rem' }}
|
||||||
|
lh={1.6}
|
||||||
|
ta="justify"
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.biodata }}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* RIWAYAT */}
|
||||||
|
<Box>
|
||||||
|
<Flex align="center" gap="sm" mb="sm">
|
||||||
|
<IconTimeline size={28} />
|
||||||
|
<Title order={3} fz={{ base: '1.3rem', md: '1.6rem' }} lh={1.2} fw={800}>
|
||||||
|
Riwayat Karir
|
||||||
|
</Title>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<List spacing="xs" size="sm">
|
||||||
|
<Box px={20}>
|
||||||
|
<Text
|
||||||
|
fz={{ base: '1rem', md: '1.1rem', lg: '1.2rem' }}
|
||||||
|
lh={1.6}
|
||||||
|
ta="justify"
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.riwayat }}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</List>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* ORGANISASI */}
|
||||||
|
<Box pb={40}>
|
||||||
|
<Flex align="center" gap="sm" mb="sm">
|
||||||
|
<IconBuildingCommunity size={28} />
|
||||||
|
<Title order={3} fz={{ base: '1.3rem', md: '1.6rem' }} lh={1.2} fw={800}>
|
||||||
|
Pengalaman Organisasi
|
||||||
|
</Title>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<List spacing="xs" size="sm">
|
||||||
|
<Box px={20}>
|
||||||
|
<Text
|
||||||
|
fz={{ base: '1rem', md: '1.1rem' }}
|
||||||
|
lh={1.6}
|
||||||
|
ta="justify"
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.pengalaman }}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</List>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* PROGRAM UNGGULAN */}
|
||||||
|
<Box>
|
||||||
|
<Flex align="center" gap="sm" mb="sm">
|
||||||
|
<IconTargetArrow size={28} />
|
||||||
|
<Title order={3} fz={{ base: '1.3rem', md: '1.6rem' }} lh={1.2} fw={800}>
|
||||||
|
Program Unggulan
|
||||||
|
</Title>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<List spacing="xs" size="sm">
|
||||||
|
<Box px={20}>
|
||||||
|
<Text
|
||||||
|
fz={{ base: '1rem', md: '1.1rem' }}
|
||||||
|
lh={1.6}
|
||||||
|
ta="justify"
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.unggulan }}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</List>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{/* tombol scroll */}
|
||||||
|
<ScrollToTopButton />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
'use client'
|
|
||||||
import stateProfilePPID from '@/app/admin/(dashboard)/_state/ppid/profile_ppid/profile_PPID';
|
|
||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Center, Divider, Flex, Image, List, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
|
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
|
||||||
import { IconBuildingCommunity, IconTargetArrow, IconTimeline, IconUser } from '@tabler/icons-react';
|
|
||||||
import { useProxy } from 'valtio/utils';
|
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
|
||||||
import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton';
|
|
||||||
|
|
||||||
function Page() {
|
|
||||||
const allList = useProxy(stateProfilePPID)
|
|
||||||
useShallowEffect(() => {
|
|
||||||
allList.profile.load("edit")
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (!allList.profile.data) return (
|
|
||||||
<Stack bg={colors.Bg} py="xl" gap="22">
|
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
|
||||||
<Skeleton h={40} />
|
|
||||||
</Box>
|
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
|
||||||
<Skeleton h={80} />
|
|
||||||
</Box>
|
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
|
||||||
<Paper p="xl" bg={colors['white-trans-1']}>
|
|
||||||
{Array.from({ length: 8 }).map((_, i) => (
|
|
||||||
<Skeleton key={i} h={40} mb="sm" />
|
|
||||||
))}
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
)
|
|
||||||
|
|
||||||
const dataArray = Array.isArray(allList.profile.data)
|
|
||||||
? allList.profile.data
|
|
||||||
: [allList.profile.data]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
|
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
|
||||||
<BackButton />
|
|
||||||
</Box>
|
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
|
||||||
<Text ta="center" fz={{ base: "2rem", md: "2.5rem", lg: "3rem", xl: "3.4rem" }} c={colors["blue-button"]} fw="bold">
|
|
||||||
Profil PPID Desa Darmasaba
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
{dataArray.map((item) => (
|
|
||||||
<Box key={item.id} px={{ base: "md", md: 100 }}>
|
|
||||||
<Paper p="xl" bg={colors['white-trans-1']} radius="lg" shadow="xl">
|
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
|
||||||
<Center>
|
|
||||||
<Image loading='lazy' src="/darmasaba-icon.png" h={{ base: 70, md: 120 }} w={{ base: 70, md: 120 }} alt="Logo Desa" />
|
|
||||||
</Center>
|
|
||||||
<Text ta="center" fz={{ base: "1.2rem", md: "2rem", lg: "2.5rem", xl: "3rem" }} fw="bold">
|
|
||||||
Pejabat Pengelola Informasi dan Dokumentasi
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
<Divider my="lg" />
|
|
||||||
|
|
||||||
<Box px={{ base: 0, md: 50 }} pb={40}>
|
|
||||||
<SimpleGrid cols={{ base: 1, xl: 2 }} spacing="xl">
|
|
||||||
<Box px={{ base: 0, md: 50 }}>
|
|
||||||
<Paper bg={colors['white-trans-1']} radius="xl" shadow="md" withBorder>
|
|
||||||
<Stack gap={0}>
|
|
||||||
<Image
|
|
||||||
pt={{ base: 0, md: 100 }}
|
|
||||||
px="lg"
|
|
||||||
src={item.image?.link ? `${item.image.link}?t=${Date.now()}` : "/perbekel.png"}
|
|
||||||
alt="Foto Pimpinan"
|
|
||||||
radius="lg"
|
|
||||||
onError={(e) => e.currentTarget.src = "/perbekel.png"}
|
|
||||||
loading="lazy"
|
|
||||||
/>
|
|
||||||
<Paper
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
px="lg"
|
|
||||||
radius="0 0 var(--mantine-radius-xl) var(--mantine-radius-xl)"
|
|
||||||
className="glass3"
|
|
||||||
py={{ base: 20, md: 50 }}
|
|
||||||
>
|
|
||||||
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "xl", md: "h2" }}>
|
|
||||||
{item.name}
|
|
||||||
</Text>
|
|
||||||
</Paper>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box>
|
|
||||||
<Stack gap="xl">
|
|
||||||
<Box>
|
|
||||||
<Flex align="center" gap="sm" mb="sm">
|
|
||||||
<IconUser size={28} />
|
|
||||||
<Text fz={{ base: "1.25rem", md: "1.5rem" }} fw="bold">Biografi</Text>
|
|
||||||
</Flex>
|
|
||||||
<Box px={20}>
|
|
||||||
<Text fz={{ base: "1rem", md: "1.125rem", lg: "1.25rem" }} ta="justify" dangerouslySetInnerHTML={{ __html: item.biodata }} style={{ wordBreak: "break-word", whiteSpace: "normal" }} />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Flex align="center" gap="sm" mb="sm">
|
|
||||||
<IconTimeline size={28} />
|
|
||||||
<Text fz={{ base: "1.25rem", md: "1.5rem" }} fw="bold">Riwayat Karir</Text>
|
|
||||||
</Flex>
|
|
||||||
<List spacing="xs" size="sm">
|
|
||||||
<Box px={20}>
|
|
||||||
<Text fz={{ base: "1rem", md: "1.125rem", lg: "1.25rem" }} dangerouslySetInnerHTML={{ __html: item.riwayat }} style={{ wordBreak: "break-word", whiteSpace: "normal" }} />
|
|
||||||
</Box>
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
</SimpleGrid>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box pb={40}>
|
|
||||||
<Flex align="center" gap="sm" mb="sm">
|
|
||||||
<IconBuildingCommunity size={28} />
|
|
||||||
<Text fz={{ base: "1.25rem", md: "1.5rem" }} fw="bold">Pengalaman Organisasi</Text>
|
|
||||||
</Flex>
|
|
||||||
<List spacing="xs" size="sm">
|
|
||||||
<Box px={20}>
|
|
||||||
<Text fz={{ base: "1rem", md: "1.125rem" }} ta="justify" dangerouslySetInnerHTML={{ __html: item.pengalaman }} style={{ wordBreak: "break-word", whiteSpace: "normal" }} />
|
|
||||||
</Box>
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box>
|
|
||||||
<Flex align="center" gap="sm" mb="sm">
|
|
||||||
<IconTargetArrow size={28} />
|
|
||||||
<Text fz={{ base: "1.25rem", md: "1.5rem" }} fw="bold">Program Unggulan</Text>
|
|
||||||
</Flex>
|
|
||||||
<List spacing="xs" size="sm">
|
|
||||||
<Box px={20}>
|
|
||||||
<Text fz={{ base: "1rem", md: "1.125rem" }} ta="justify" dangerouslySetInnerHTML={{ __html: item.unggulan }} style={{ wordBreak: "break-word", whiteSpace: "normal" }} />
|
|
||||||
</Box>
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
{/* Tombol Scroll ke Atas */}
|
|
||||||
<ScrollToTopButton />
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Page
|
|
||||||
@@ -27,7 +27,6 @@ function DetailPegawaiUser() {
|
|||||||
statePegawai.findUnique.load(params?.id as string);
|
statePegawai.findUnique.load(params?.id as string);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
if (!statePegawai.findUnique.data) {
|
if (!statePegawai.findUnique.data) {
|
||||||
return (
|
return (
|
||||||
<Stack py="lg">
|
<Stack py="lg">
|
||||||
@@ -52,7 +51,7 @@ function DetailPegawaiUser() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconArrowBack size={22} color={colors['blue-button']} />
|
<IconArrowBack size={22} color={colors['blue-button']} />
|
||||||
<Text c={colors['blue-button']} fw={500}>
|
<Text fz={{ base: 'sm', md: 'md' }} lh="1.4" fw={500} c={colors['blue-button']}>
|
||||||
Kembali
|
Kembali
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -65,9 +64,7 @@ function DetailPegawaiUser() {
|
|||||||
radius="lg"
|
radius="lg"
|
||||||
shadow="sm"
|
shadow="sm"
|
||||||
bg="white"
|
bg="white"
|
||||||
style={{
|
style={{ border: '1px solid #eaeaea' }}
|
||||||
border: '1px solid #eaeaea',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Stack align="center" gap="md">
|
<Stack align="center" gap="md">
|
||||||
{/* Foto Profil */}
|
{/* Foto Profil */}
|
||||||
@@ -84,10 +81,23 @@ function DetailPegawaiUser() {
|
|||||||
|
|
||||||
{/* Nama & Jabatan */}
|
{/* Nama & Jabatan */}
|
||||||
<Stack align="center" gap={2}>
|
<Stack align="center" gap={2}>
|
||||||
<Title order={3} fw={700} c={colors['blue-button']}>
|
<Title
|
||||||
|
order={2}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
fw={700}
|
||||||
|
fz={{ base: 'xl', md: '28px' }}
|
||||||
|
lh="1.2"
|
||||||
|
ta="center"
|
||||||
|
>
|
||||||
{data.namaLengkap || '-'} {data.gelarAkademik || ''}
|
{data.namaLengkap || '-'} {data.gelarAkademik || ''}
|
||||||
</Title>
|
</Title>
|
||||||
<Text fz="sm" c="dimmed">
|
|
||||||
|
<Text
|
||||||
|
fz={{ base: 'sm', md: 'md' }}
|
||||||
|
lh="1.4"
|
||||||
|
c="dimmed"
|
||||||
|
ta="center"
|
||||||
|
>
|
||||||
{data.posisi?.nama || 'Posisi tidak tersedia'}
|
{data.posisi?.nama || 'Posisi tidak tersedia'}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -123,7 +133,7 @@ function DetailPegawaiUser() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Komponen kecil untuk menampilkan baris informasi */
|
/* Komponen Baris Informasi */
|
||||||
function InfoRow({
|
function InfoRow({
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
@@ -137,11 +147,18 @@ function InfoRow({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="sm" fw={600} c="dark">
|
<Text
|
||||||
|
fz={{ base: 'sm', md: 'md' }}
|
||||||
|
fw={600}
|
||||||
|
lh="1.3"
|
||||||
|
c="dark"
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
fz="sm"
|
fz={{ base: 'sm', md: 'md' }}
|
||||||
|
lh="1.5"
|
||||||
c={valueColor || 'dimmed'}
|
c={valueColor || 'dimmed'}
|
||||||
style={{
|
style={{
|
||||||
whiteSpace: multiline ? 'normal' : 'nowrap',
|
whiteSpace: multiline ? 'normal' : 'nowrap',
|
||||||
|
|||||||
@@ -59,10 +59,11 @@ export default function Page() {
|
|||||||
ta="center"
|
ta="center"
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
fz={{ base: 28, md: 36, lg: 44 }}
|
fz={{ base: 28, md: 36, lg: 44 }}
|
||||||
|
lh={{ base: 1.05, md: 1.03 }}
|
||||||
>
|
>
|
||||||
Struktur Organisasi PPID
|
Struktur Organisasi PPID
|
||||||
</Title>
|
</Title>
|
||||||
<Text ta="center" c="black" maw={800}>
|
<Text ta="center" c="black" maw={800} fz={{ base: 13, md: 15 }} lh={1.45}>
|
||||||
Gambaran visual peran dan pegawai yang ditugaskan. Arahkan kursor
|
Gambaran visual peran dan pegawai yang ditugaskan. Arahkan kursor
|
||||||
untuk melihat detail atau klik node untuk fokus tampilan.
|
untuk melihat detail atau klik node untuk fokus tampilan.
|
||||||
</Text>
|
</Text>
|
||||||
@@ -105,8 +106,8 @@ function StrukturOrganisasiPPID() {
|
|||||||
<Center py={48}>
|
<Center py={48}>
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<Loader size="lg" />
|
<Loader size="lg" />
|
||||||
<Text fw={600}>Memuat struktur organisasi…</Text>
|
<Text fw={600} fz={{ base: 15, md: 16 }} lh={1.2}>Memuat struktur organisasi…</Text>
|
||||||
<Text c="dimmed" size="sm">
|
<Text c="dimmed" fz={{ base: 12, md: 13 }} lh={1.4}>
|
||||||
Mengambil data pegawai dan posisi. Mohon tunggu sebentar.
|
Mengambil data pegawai dan posisi. Mohon tunggu sebentar.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -132,10 +133,10 @@ function StrukturOrganisasiPPID() {
|
|||||||
<Center>
|
<Center>
|
||||||
<IconUsers size={56} />
|
<IconUsers size={56} />
|
||||||
</Center>
|
</Center>
|
||||||
<Title order={3} mt="md">
|
<Title order={3} mt="md" fz={{ base: 16, md: 18 }} lh={1.15}>
|
||||||
Data pegawai belum tersedia
|
Data pegawai belum tersedia
|
||||||
</Title>
|
</Title>
|
||||||
<Text c="dimmed" mt="xs">
|
<Text c="dimmed" mt="xs" fz={{ base: 13, md: 14 }} lh={1.4}>
|
||||||
Belum ada data pegawai yang tercatat untuk PPID.
|
Belum ada data pegawai yang tercatat untuk PPID.
|
||||||
</Text>
|
</Text>
|
||||||
<Group justify="center" mt="lg">
|
<Group justify="center" mt="lg">
|
||||||
@@ -232,11 +233,18 @@ function StrukturOrganisasiPPID() {
|
|||||||
{/* 🔍 Controls */}
|
{/* 🔍 Controls */}
|
||||||
<Paper
|
<Paper
|
||||||
shadow="xs"
|
shadow="xs"
|
||||||
|
w={{
|
||||||
|
base: '100%', // Mobile: 100%
|
||||||
|
sm: '40%', // Tablet: 95%
|
||||||
|
md: '39%', // Desktop: 70%
|
||||||
|
lg: '38%', // Desktop L: 60%
|
||||||
|
xl: '37%', // 4K: 50%
|
||||||
|
'2xl': '36%', // Ultra-wide: 45%
|
||||||
|
}}
|
||||||
p="md"
|
p="md"
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{
|
style={{
|
||||||
background: colors['blue-button'],
|
background: colors['blue-button'], // ⬅️ penting
|
||||||
width: '100%', // ⬅️ penting
|
|
||||||
maxWidth: '100%', // ⬅️ penting
|
maxWidth: '100%', // ⬅️ penting
|
||||||
overflowX: 'auto' // ⬅️ untuk mencegah overflow
|
overflowX: 'auto' // ⬅️ untuk mencegah overflow
|
||||||
}}
|
}}
|
||||||
@@ -269,30 +277,33 @@ function StrukturOrganisasiPPID() {
|
|||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
padding: '6px 12px',
|
padding: '6px 12px',
|
||||||
minHeight: 'auto',
|
minHeight: 'auto',
|
||||||
flexShrink: 0, // 👈 PENTING: mencegah tab mengecil
|
flexShrink: 0,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
style={{ width: '100%' }} // 👈 penting
|
||||||
>
|
>
|
||||||
<TabsList
|
<TabsList
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
overflowX: 'auto',
|
overflowX: 'auto',
|
||||||
overflowY: 'hidden', // 👈 tambahkan ini
|
overflowY: 'hidden',
|
||||||
gap: '4px',
|
gap: '4px',
|
||||||
paddingBottom: '4px',
|
paddingBottom: '4px',
|
||||||
flexWrap: 'nowrap',
|
flexWrap: 'nowrap',
|
||||||
WebkitOverflowScrolling: 'touch', // 👈 smooth scroll di iOS
|
WebkitOverflowScrolling: 'touch',
|
||||||
scrollbarWidth: 'thin', // 👈 scrollbar tipis di Firefox
|
scrollbarWidth: 'thin',
|
||||||
msOverflowStyle: '-ms-autohiding-scrollbar', // 👈 untuk IE/Edge
|
msOverflowStyle: '-ms-autohiding-scrollbar',
|
||||||
|
maxWidth: '100%',
|
||||||
|
scrollBehavior: 'smooth', // 👈 smooth scroll
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TabsTab
|
<TabsTab
|
||||||
value="zoom-out"
|
value="zoom-out"
|
||||||
onClick={handleZoomOut}
|
onClick={handleZoomOut}
|
||||||
leftSection={<IconZoomOut size={16} />}
|
leftSection={<IconZoomOut size={16} />}
|
||||||
style={{ flexShrink: 0 }} // 👈 pastikan tidak mengecil
|
style={{ flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
Zoom Out
|
<Text fz={{ base: 12, sm: 13 }} lh={1} ta="center">Zoom Out</Text>
|
||||||
</TabsTab>
|
</TabsTab>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
@@ -301,7 +312,6 @@ function StrukturOrganisasiPPID() {
|
|||||||
px={12}
|
px={12}
|
||||||
py={6}
|
py={6}
|
||||||
style={{
|
style={{
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
borderRadius: '6px',
|
borderRadius: '6px',
|
||||||
minWidth: 60,
|
minWidth: 60,
|
||||||
@@ -310,10 +320,12 @@ function StrukturOrganisasiPPID() {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
whiteSpace: 'nowrap', // 👈 mencegah text wrap
|
whiteSpace: 'nowrap',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Text fz={{ base: 12, sm: 13 }} lh={1} c={colors['blue-button']}>
|
||||||
{Math.round(scale * 100)}%
|
{Math.round(scale * 100)}%
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<TabsTab
|
<TabsTab
|
||||||
@@ -322,7 +334,7 @@ function StrukturOrganisasiPPID() {
|
|||||||
leftSection={<IconZoomIn size={16} />}
|
leftSection={<IconZoomIn size={16} />}
|
||||||
style={{ flexShrink: 0 }}
|
style={{ flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
Zoom In
|
<Text fz={{ base: 12, sm: 13 }} lh={1} ta="center">Zoom In</Text>
|
||||||
</TabsTab>
|
</TabsTab>
|
||||||
|
|
||||||
<TabsTab
|
<TabsTab
|
||||||
@@ -330,7 +342,7 @@ function StrukturOrganisasiPPID() {
|
|||||||
onClick={resetZoom}
|
onClick={resetZoom}
|
||||||
style={{ flexShrink: 0 }}
|
style={{ flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
Reset
|
<Text fz={{ base: 12, sm: 13 }} lh={1} ta="center">Reset</Text>
|
||||||
</TabsTab>
|
</TabsTab>
|
||||||
|
|
||||||
<TabsTab
|
<TabsTab
|
||||||
@@ -345,7 +357,9 @@ function StrukturOrganisasiPPID() {
|
|||||||
}
|
}
|
||||||
style={{ flexShrink: 0 }}
|
style={{ flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
|
<Text fz={{ base: 12, sm: 13 }} lh={1} ta="center">
|
||||||
{isFullscreen ? 'Exit' : 'Fullscreen'}
|
{isFullscreen ? 'Exit' : 'Fullscreen'}
|
||||||
|
</Text>
|
||||||
</TabsTab>
|
</TabsTab>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
@@ -451,18 +465,17 @@ function NodeCard({ node, router }: any) {
|
|||||||
{/* Name */}
|
{/* Name */}
|
||||||
<Text
|
<Text
|
||||||
fw={700}
|
fw={700}
|
||||||
size="sm"
|
|
||||||
ta="center"
|
ta="center"
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
lineClamp={2}
|
lineClamp={2}
|
||||||
|
fz={{ base: 13, md: 15 }}
|
||||||
|
lh={1.2}
|
||||||
style={{
|
style={{
|
||||||
// fontSize: 'clamp(12px, 4vw, 16px)', // 👈 responsif font size
|
|
||||||
minHeight: 40,
|
minHeight: 40,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
wordBreak: 'break-word',
|
wordBreak: 'break-word',
|
||||||
lineHeight: 1.3,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
@@ -470,18 +483,18 @@ function NodeCard({ node, router }: any) {
|
|||||||
|
|
||||||
{/* Title/Position */}
|
{/* Title/Position */}
|
||||||
<Text
|
<Text
|
||||||
size="xs"
|
|
||||||
c="dimmed"
|
c="dimmed"
|
||||||
ta="center"
|
ta="center"
|
||||||
fw={500}
|
fw={500}
|
||||||
lineClamp={2}
|
lineClamp={2}
|
||||||
|
fz={{ base: 12, md: 13 }}
|
||||||
|
lh={1.3}
|
||||||
style={{
|
style={{
|
||||||
minHeight: 32,
|
minHeight: 32,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
wordBreak: 'break-word',
|
wordBreak: 'break-word',
|
||||||
lineHeight: 1.2,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
@@ -504,7 +517,7 @@ function NodeCard({ node, router }: any) {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Lihat Detail
|
<Text fz={{ base: 12, md: 13 }} lh={1} ta="center">Lihat Detail</Text>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,7 +1,18 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import stateVisiMisiPPID from '@/app/admin/(dashboard)/_state/ppid/visi_misi_ppid/visimisiPPID';
|
import stateVisiMisiPPID from '@/app/admin/(dashboard)/_state/ppid/visi_misi_ppid/visimisiPPID';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Center, Image, Paper, Skeleton, Stack, Text, Divider, Transition } from '@mantine/core';
|
import {
|
||||||
|
Box,
|
||||||
|
Center,
|
||||||
|
Image,
|
||||||
|
Paper,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Divider,
|
||||||
|
Transition,
|
||||||
|
Title
|
||||||
|
} from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import { IconSparkles } from '@tabler/icons-react';
|
import { IconSparkles } from '@tabler/icons-react';
|
||||||
@@ -9,6 +20,7 @@ import BackButton from '../../desa/layanan/_com/BackButto';
|
|||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
const allList = useProxy(stateVisiMisiPPID);
|
const allList = useProxy(stateVisiMisiPPID);
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
allList.findById.load("1");
|
allList.findById.load("1");
|
||||||
}, []);
|
}, []);
|
||||||
@@ -35,7 +47,7 @@ function Page() {
|
|||||||
|
|
||||||
{dataArray.map((item) => (
|
{dataArray.map((item) => (
|
||||||
<Box key={item.id} px={{ base: 'md', md: 100 }}>
|
<Box key={item.id} px={{ base: 'md', md: 100 }}>
|
||||||
<Transition mounted={true} transition="fade" duration={500} timingFunction="ease">
|
<Transition mounted transition="fade" duration={500} timingFunction="ease">
|
||||||
{(styles) => (
|
{(styles) => (
|
||||||
<Paper
|
<Paper
|
||||||
style={styles}
|
style={styles}
|
||||||
@@ -46,56 +58,93 @@ function Page() {
|
|||||||
withBorder
|
withBorder
|
||||||
>
|
>
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
|
|
||||||
|
{/* ==== MOTTO SECTION ==== */}
|
||||||
<Box>
|
<Box>
|
||||||
<Center mb="md">
|
<Center mb="md">
|
||||||
<Image src="/darmasaba-icon.png" w={{ base: 80, md: 130 }} alt="Logo Desa Darmasaba" loading='lazy' />
|
<Image
|
||||||
|
src="/darmasaba-icon.png"
|
||||||
|
w={{ base: 80, md: 130 }}
|
||||||
|
alt="Logo Desa Darmasaba"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
<Text
|
|
||||||
|
<Title
|
||||||
|
order={2}
|
||||||
ta="center"
|
ta="center"
|
||||||
fz={{ base: 28, md: 36 }}
|
fz={{ base: 26, md: 34 }}
|
||||||
fw={800}
|
lh={1.2}
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
>
|
>
|
||||||
Moto PPID Desa Darmasaba
|
Moto PPID Desa Darmasaba
|
||||||
</Text>
|
</Title>
|
||||||
<Text ta="center" fz={{ base: 16, md: 20 }} mt="xs">
|
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: 15, md: 18 }}
|
||||||
|
lh={1.5}
|
||||||
|
c={"black"}
|
||||||
|
mt="xs"
|
||||||
|
>
|
||||||
Memberikan informasi yang cepat, mudah, tepat, dan transparan
|
Memberikan informasi yang cepat, mudah, tepat, dan transparan
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Divider my="sm" labelPosition="center" label={<IconSparkles size={18} />} />
|
<Divider
|
||||||
|
my="sm"
|
||||||
|
labelPosition="center"
|
||||||
|
label={<IconSparkles size={18} />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ==== VISI SECTION ==== */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text ta="center" fz={{ base: 24, md: 30 }} fw={800}
|
<Title
|
||||||
c={colors['blue-button']} mb="sm">
|
order={3}
|
||||||
Visi PPID
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
fz={{ base: 'md', md: 'lg' }}
|
|
||||||
lh={1.7}
|
|
||||||
ta="center"
|
ta="center"
|
||||||
|
fz={{ base: 22, md: 28 }}
|
||||||
|
lh={1.2}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
mb="sm"
|
||||||
|
>
|
||||||
|
Visi PPID
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: 15, md: 18 }}
|
||||||
|
lh={1.7}
|
||||||
dangerouslySetInnerHTML={{ __html: item.visi }}
|
dangerouslySetInnerHTML={{ __html: item.visi }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Divider my="sm" />
|
<Divider my="sm" />
|
||||||
|
|
||||||
|
{/* ==== MISI SECTION ==== */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text ta="center" fz={{ base: 24, md: 30 }} fw={800}
|
<Title
|
||||||
c={colors['blue-button']} mb="sm">
|
order={3}
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: 22, md: 28 }}
|
||||||
|
lh={1.2}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
mb="sm"
|
||||||
|
>
|
||||||
Misi PPID
|
Misi PPID
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Text
|
<Text
|
||||||
ta={"justify"}
|
ta="justify"
|
||||||
fz={{ base: 'md', md: 'lg' }}
|
fz={{ base: 15, md: 18 }}
|
||||||
lh={1.7}
|
lh={1.7}
|
||||||
dangerouslySetInnerHTML={{ __html: item.misi }}
|
dangerouslySetInnerHTML={{ __html: item.misi }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ export default function ModernNewsNotification({
|
|||||||
position: "fixed",
|
position: "fixed",
|
||||||
bottom: "24px",
|
bottom: "24px",
|
||||||
right: "24px",
|
right: "24px",
|
||||||
|
zIndex: 1
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ const NewsReaderLanding = () => {
|
|||||||
borderBottomRightRadius: '20px',
|
borderBottomRightRadius: '20px',
|
||||||
borderTopRightRadius: '20px',
|
borderTopRightRadius: '20px',
|
||||||
transition: 'all 0.3s ease',
|
transition: 'all 0.3s ease',
|
||||||
|
zIndex: 1
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isPointerMode ? <IconMusicOff /> : <IconMusic />}
|
{isPointerMode ? <IconMusicOff /> : <IconMusic />}
|
||||||
|
|||||||
@@ -5,7 +5,20 @@ import apbdes from '@/app/admin/(dashboard)/_state/landing-page/apbdes'
|
|||||||
import APBDesProgress from '@/app/darmasaba/(tambahan)/apbdes/lib/apbDesaProgress'
|
import APBDesProgress from '@/app/darmasaba/(tambahan)/apbdes/lib/apbDesaProgress'
|
||||||
import { transformAPBDesData } from '@/app/darmasaba/(tambahan)/apbdes/lib/types'
|
import { transformAPBDesData } from '@/app/darmasaba/(tambahan)/apbdes/lib/types'
|
||||||
import colors from '@/con/colors'
|
import colors from '@/con/colors'
|
||||||
import { ActionIcon, BackgroundImage, Box, Button, Center, Group, Loader, Select, SimpleGrid, Stack, Text } from '@mantine/core'
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
BackgroundImage,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Group,
|
||||||
|
Loader,
|
||||||
|
Select,
|
||||||
|
SimpleGrid,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
} from '@mantine/core'
|
||||||
import { IconDownload } from '@tabler/icons-react'
|
import { IconDownload } from '@tabler/icons-react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
@@ -38,17 +51,15 @@ function Apbdes() {
|
|||||||
const dataAPBDes = state.findMany.data || []
|
const dataAPBDes = state.findMany.data || []
|
||||||
|
|
||||||
const years = Array.from(new Set(dataAPBDes.map((item: any) => item.tahun)))
|
const years = Array.from(new Set(dataAPBDes.map((item: any) => item.tahun)))
|
||||||
.sort((a, b) => b - a) // urutkan descending
|
.sort((a, b) => b - a)
|
||||||
.map(year => ({ value: year.toString(), label: `Tahun ${year}` }))
|
.map(year => ({ value: year.toString(), label: `Tahun ${year}` }))
|
||||||
|
|
||||||
// Pilih tahun pertama sebagai default jika belum ada yang dipilih
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (years.length > 0 && !selectedYear) {
|
if (years.length > 0 && !selectedYear) {
|
||||||
setSelectedYear(years[0].value)
|
setSelectedYear(years[0].value)
|
||||||
}
|
}
|
||||||
}, [years, selectedYear])
|
}, [years, selectedYear])
|
||||||
|
|
||||||
// Transform and filter data based on selected year
|
|
||||||
const currentApbdes = dataAPBDes.length > 0
|
const currentApbdes = dataAPBDes.length > 0
|
||||||
? transformAPBDesData(dataAPBDes.find(item => item?.tahun?.toString() === selectedYear) || dataAPBDes[0])
|
? transformAPBDesData(dataAPBDes.find(item => item?.tahun?.toString() === selectedYear) || dataAPBDes[0])
|
||||||
: null
|
: null
|
||||||
@@ -57,17 +68,31 @@ function Apbdes() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p="sm" gap="xl" bg={colors.Bg}>
|
<Stack p="sm" gap="xl" bg={colors.Bg}>
|
||||||
<Box mt={"xl"}>
|
{/* 📌 HEADING */}
|
||||||
|
<Box mt="xl">
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Text c={colors["blue-button"]} ta={"center"} fw={"bold"} fz={{ base: "1.8rem", md: "3.4rem" }}>
|
<Title
|
||||||
|
order={1}
|
||||||
|
ta="center"
|
||||||
|
c={colors['blue-button']}
|
||||||
|
fz={{ base: '2rem', md: '3.6rem' }}
|
||||||
|
lh={{ base: 1.2, md: 1.1 }}
|
||||||
|
>
|
||||||
{textHeading.title}
|
{textHeading.title}
|
||||||
</Text>
|
</Title>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>
|
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: '1rem', md: '1.25rem' }}
|
||||||
|
lh={{ base: 1.5, md: 1.55 }}
|
||||||
|
c="black"
|
||||||
|
>
|
||||||
{textHeading.des}
|
{textHeading.des}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Button Lihat Semua */}
|
||||||
<Group justify="center">
|
<Group justify="center">
|
||||||
<Button
|
<Button
|
||||||
component={Link}
|
component={Link}
|
||||||
@@ -81,32 +106,39 @@ function Apbdes() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
{/* 🔥 COMBOBOX UNTUK PILIH TAHUN */}
|
{/* COMBOBOX */}
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Select
|
<Select
|
||||||
label="Pilih Tahun APBDes"
|
label={<Text fw={600} fz="sm">Pilih Tahun APBDes</Text>}
|
||||||
placeholder="Pilih tahun"
|
placeholder="Pilih tahun"
|
||||||
value={selectedYear}
|
value={selectedYear}
|
||||||
onChange={setSelectedYear}
|
onChange={setSelectedYear}
|
||||||
data={years}
|
data={years}
|
||||||
w={{ base: '100%', sm: 200 }}
|
w={{ base: '100%', sm: 220 }}
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
nothingFoundMessage="Tidak ada tahun tersedia"
|
nothingFoundMessage="Tidak ada tahun tersedia"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Progress */}
|
||||||
{currentApbdes ? (
|
{currentApbdes ? (
|
||||||
<>
|
|
||||||
<APBDesProgress apbdesData={currentApbdes} />
|
<APBDesProgress apbdesData={currentApbdes} />
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<Box px={{ base: 'md', md: 100 }} py="md">
|
<Box px={{ base: 'md', md: 100 }} py="md">
|
||||||
<Text c="dimmed">Tidak ada data APBDes untuk tahun yang dipilih.</Text>
|
<Text fz="sm" c="dimmed" ta="center" lh={1.5}>
|
||||||
|
Tidak ada data APBDes untuk tahun yang dipilih.
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SimpleGrid mx={{ base: 'md', md: 100 }} cols={{ base: 1, sm: 3 }} spacing="lg" pb={"xl"}>
|
{/* GRID */}
|
||||||
|
<SimpleGrid
|
||||||
|
mx={{ base: 'md', md: 100 }}
|
||||||
|
cols={{ base: 1, sm: 3 }}
|
||||||
|
spacing="lg"
|
||||||
|
pb="xl"
|
||||||
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Center mih={200}>
|
<Center mih={200}>
|
||||||
<Loader size="lg" color="blue" />
|
<Loader size="lg" color="blue" />
|
||||||
@@ -114,10 +146,10 @@ function Apbdes() {
|
|||||||
) : data.length === 0 ? (
|
) : data.length === 0 ? (
|
||||||
<Center mih={200}>
|
<Center mih={200}>
|
||||||
<Stack align="center" gap="xs">
|
<Stack align="center" gap="xs">
|
||||||
<Text fz="lg" c="dimmed">
|
<Text fz="lg" c="dimmed" lh={1.4}>
|
||||||
Belum ada data APBDes yang tersedia
|
Belum ada data APBDes yang tersedia
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed" lh={1.4}>
|
||||||
Data akan ditampilkan di sini setelah diunggah
|
Data akan ditampilkan di sini setelah diunggah
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -133,25 +165,30 @@ function Apbdes() {
|
|||||||
style={{ overflow: 'hidden' }}
|
style={{ overflow: 'hidden' }}
|
||||||
>
|
>
|
||||||
<Box pos="absolute" inset={0} bg="rgba(0,0,0,0.45)" style={{ borderRadius: 16 }} />
|
<Box pos="absolute" inset={0} bg="rgba(0,0,0,0.45)" style={{ borderRadius: 16 }} />
|
||||||
<Stack gap={"xs"} justify="space-between" h="100%" p="xl" pos="relative">
|
|
||||||
|
<Stack gap="xs" justify="space-between" h="100%" p="xl" pos="relative">
|
||||||
<Text
|
<Text
|
||||||
c="white"
|
c="white"
|
||||||
fw={600}
|
fw={600}
|
||||||
fz="lg"
|
fz={{ base: 'lg', md: 'xl' }}
|
||||||
ta="center"
|
ta="center"
|
||||||
|
lh={1.35}
|
||||||
lineClamp={2}
|
lineClamp={2}
|
||||||
>
|
>
|
||||||
{v.name}
|
{v.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
fw="bold"
|
fw={700}
|
||||||
c="white"
|
c="white"
|
||||||
fz="3rem"
|
fz={{ base: '2.4rem', md: '3.2rem' }}
|
||||||
ta="center"
|
ta="center"
|
||||||
|
lh={1}
|
||||||
style={{ textShadow: '0 2px 8px rgba(0,0,0,0.6)' }}
|
style={{ textShadow: '0 2px 8px rgba(0,0,0,0.6)' }}
|
||||||
>
|
>
|
||||||
{v.jumlah}
|
{v.jumlah}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
component={Link}
|
component={Link}
|
||||||
@@ -163,29 +200,12 @@ function Apbdes() {
|
|||||||
>
|
>
|
||||||
<IconDownload size={20} color="white" />
|
<IconDownload size={20} color="white" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
|
|
||||||
</Center>
|
</Center>
|
||||||
{/* <Group justify="center">
|
|
||||||
<ActionIcon
|
|
||||||
component={Link}
|
|
||||||
href={v.file?.link || ''}
|
|
||||||
radius="xl"
|
|
||||||
size="lg"
|
|
||||||
variant="gradient"
|
|
||||||
gradient={{ from: '#1C6EA4', to: '#1C6EA4' }}
|
|
||||||
>
|
|
||||||
<Group align="center" gap="xs" px="md" py={6}>
|
|
||||||
<IconDownload size={25} color="white" />
|
|
||||||
</Group>
|
|
||||||
</ActionIcon>
|
|
||||||
</Group> */}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</BackgroundImage>
|
</BackgroundImage>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
|
||||||
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,16 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import korupsiState from "@/app/admin/(dashboard)/_state/landing-page/desa-anti-korupsi";
|
import korupsiState from "@/app/admin/(dashboard)/_state/landing-page/desa-anti-korupsi";
|
||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
import { Button, Center, Container, Flex, Paper, SimpleGrid, Stack, Text } from "@mantine/core";
|
import {
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Container,
|
||||||
|
Flex,
|
||||||
|
Paper,
|
||||||
|
SimpleGrid,
|
||||||
|
Stack,
|
||||||
|
Text
|
||||||
|
} from "@mantine/core";
|
||||||
import { IconClipboardText } from "@tabler/icons-react";
|
import { IconClipboardText } from "@tabler/icons-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -12,37 +21,70 @@ function DesaAntiKorupsi() {
|
|||||||
const state = useProxy(korupsiState);
|
const state = useProxy(korupsiState);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
await state.desaAntikorupsi.findMany.load();
|
await state.desaAntikorupsi.findMany.load();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading data:', error);
|
console.error("Error loading data:", error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
loadData();
|
loadData();
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const data = (state.desaAntikorupsi.findMany.data || []).slice(0, 6);
|
const data = (state.desaAntikorupsi.findMany.data || []).slice(0, 6);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap={"0"} bg={colors.Bg} p={"sm"} my={"xs"}>
|
<Stack gap="0" bg={colors.Bg} p="sm" my="xs">
|
||||||
<Container w={{ base: "100%", md: "80%" }} p={"md"} >
|
{/* ===================== HEADER ===================== */}
|
||||||
|
<Container w={{ base: "100%", md: "80%" }} p="md">
|
||||||
<Center>
|
<Center>
|
||||||
<Text fw={"bold"} c={colors["blue-button"]} fz={{ base: "1.8rem", md: "3.4rem" }}>Desa Anti Korupsi</Text>
|
<Text
|
||||||
|
fw={700}
|
||||||
|
ta="center"
|
||||||
|
c={colors["blue-button"]}
|
||||||
|
fz={{ base: "1.8rem", md: "3.2rem" }}
|
||||||
|
lh={{ base: "2.2rem", md: "3.4rem" }}
|
||||||
|
>
|
||||||
|
Desa Anti Korupsi
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>Desa antikorupsi mendorong pemerintahan jujur dan transparan. Keuangan desa dikelola terbuka dengan melibatkan warga mengawasi anggaran, sehingga digunakan tepat sasaran sesuai kebutuhan.</Text>
|
|
||||||
<Center py={20}>
|
<Text
|
||||||
<Button radius={"lg"} fz={"h4"} bg={colors["blue-button"]} component={Link} href={"/darmasaba/desa-anti-korupsi/detail"}>Selengkapnya</Button>
|
ta="center"
|
||||||
|
c="black"
|
||||||
|
fz={{ base: "1rem", md: "1.25rem" }}
|
||||||
|
lh={{ base: "1.5rem", md: "1.8rem" }}
|
||||||
|
mt="sm"
|
||||||
|
>
|
||||||
|
Desa antikorupsi mendorong pemerintahan jujur dan transparan.
|
||||||
|
Keuangan desa dikelola secara terbuka dengan melibatkan warga
|
||||||
|
dalam pengawasan anggaran, sehingga digunakan tepat sasaran dan
|
||||||
|
sesuai kebutuhan masyarakat.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Center py={25}>
|
||||||
|
<Button
|
||||||
|
radius="lg"
|
||||||
|
fz={{ base: "md", md: "lg" }}
|
||||||
|
bg={colors["blue-button"]}
|
||||||
|
component={Link}
|
||||||
|
href="/darmasaba/desa-anti-korupsi/detail"
|
||||||
|
style={{ paddingInline: "2rem" }}
|
||||||
|
>
|
||||||
|
Selengkapnya
|
||||||
|
</Button>
|
||||||
</Center>
|
</Center>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
|
{/* ===================== LIST ===================== */}
|
||||||
<Container w="100%" maw="80rem" px="md">
|
<Container w="100%" maw="80rem" px="md">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Center mih={200}>
|
<Center mih={200}>
|
||||||
<Text fz="lg">Memuat Data...</Text>
|
<Text fz={{ base: "md", md: "lg" }}>Memuat Data...</Text>
|
||||||
</Center>
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
@@ -64,26 +106,35 @@ function DesaAntiKorupsi() {
|
|||||||
<IconClipboardText
|
<IconClipboardText
|
||||||
color={colors["blue-button"]}
|
color={colors["blue-button"]}
|
||||||
size={40}
|
size={40}
|
||||||
style={{ flexShrink: 0 }} // biar icon nggak ketekan
|
style={{ flexShrink: 0 }}
|
||||||
/>
|
/>
|
||||||
<Stack gap={2} style={{ flex: 1, minWidth: 0 }}>
|
|
||||||
|
<Stack gap={6} style={{ flex: 1, minWidth: 0 }}>
|
||||||
|
{/* Title */}
|
||||||
<Text
|
<Text
|
||||||
fz={{ base: "sm", sm: "md", md: "lg", lg: "xl" }} // lebih besar di desktop
|
fw={700}
|
||||||
c={colors["blue-button"]}
|
c={colors["blue-button"]}
|
||||||
fw={600}
|
fz={{ base: "1rem", sm: "1.1rem", md: "1.25rem" }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
lh={{ base: "1.3rem", md: "1.5rem" }}
|
||||||
|
style={{
|
||||||
|
wordBreak: "break-word",
|
||||||
|
whiteSpace: "normal"
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{v.kategori?.name || "Kategori"}
|
{v.kategori?.name || "Kategori"}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
<Text
|
<Text
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: v.name || "Name",
|
__html: v.name || "Name"
|
||||||
}}
|
}}
|
||||||
fz={{ base: "sm", sm: "md", md: "lg", lg: "xl" }} // sama, scaling responsif
|
|
||||||
c="dark"
|
c="dark"
|
||||||
|
fz={{ base: "0.9rem", sm: "1rem", md: "1.15rem" }}
|
||||||
|
lh={{ base: "1.3rem", md: "1.6rem" }}
|
||||||
style={{
|
style={{
|
||||||
wordBreak: "break-word",
|
wordBreak: "break-word",
|
||||||
whiteSpace: "normal",
|
whiteSpace: "normal"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -91,7 +142,6 @@ function DesaAntiKorupsi() {
|
|||||||
</Paper>
|
</Paper>
|
||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ interface ChartDataItem {
|
|||||||
label?: string;
|
label?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function Kepuasan() {
|
function Kepuasan() {
|
||||||
const state = useProxy(indeksKepuasanState.responden);
|
const state = useProxy(indeksKepuasanState.responden);
|
||||||
const { data, loading } = state.findMany;
|
const { data, loading } = state.findMany;
|
||||||
@@ -154,42 +152,91 @@ function Kepuasan() {
|
|||||||
|
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Stack p="sm" my={"xs"}>
|
<Stack p="sm" my="xs">
|
||||||
<Container w={{ base: "100%", md: "80%" }} p={"sm"}>
|
<Container w={{ base: "100%", md: "80%" }} p="sm">
|
||||||
<Center>
|
<Center>
|
||||||
<Text
|
<Title
|
||||||
|
order={2}
|
||||||
ta="center"
|
ta="center"
|
||||||
fz={{ base: '2rem', md: '2.8rem' }}
|
fz={{ base: '2rem', md: '2.8rem' }}
|
||||||
|
lh={{ base: 1.05, md: 1.04 }}
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
fw={800}
|
fw={800}
|
||||||
style={{ letterSpacing: '-0.5px' }}
|
style={{ letterSpacing: '-0.5px' }}
|
||||||
>Indeks Kepuasan Masyarakat</Text>
|
>
|
||||||
|
Indeks Kepuasan Masyarakat
|
||||||
|
</Title>
|
||||||
</Center>
|
</Center>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!</Text>
|
|
||||||
<Center mt={10}>
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: "0.95rem", md: "1.25rem" }}
|
||||||
|
lh={{ base: 1.45, md: 1.5 }}
|
||||||
|
c="black"
|
||||||
|
mt="sm"
|
||||||
|
>
|
||||||
|
Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Center mt={12}>
|
||||||
<Button
|
<Button
|
||||||
radius={"lg"}
|
radius="lg"
|
||||||
onClick={open}
|
onClick={open}
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
gradient={{ from: "#26667F", to: "#124170" }}
|
gradient={{ from: "#26667F", to: "#124170" }}
|
||||||
>Ajukan Responden</Button>
|
style={{ paddingLeft: 20, paddingRight: 20, fontWeight: 600 }}
|
||||||
|
>
|
||||||
|
<Text fz={{ base: "0.95rem", md: "1rem" }} ta="center" c="white">Ajukan Responden</Text>
|
||||||
|
</Button>
|
||||||
</Center>
|
</Center>
|
||||||
</Container>
|
</Container>
|
||||||
<Box px={"sm"}>
|
|
||||||
<Paper p={"lg"} bg={colors.Bg}>
|
<Box px="sm">
|
||||||
<Paper p={"lg"}>
|
<Paper p="lg" bg={colors.Bg}>
|
||||||
<Stack gap={"xs"}>
|
<Paper p="lg">
|
||||||
<Flex justify={"space-between"} align={"center"}>
|
<Stack gap="xs">
|
||||||
<Text fw={"bold"}>Pelayanan Terhadap Publik Desa Darmasaba</Text>
|
<Flex
|
||||||
<Box>
|
direction={{ base: "column", sm: "row" }}
|
||||||
<Text fz={"sm"} fw={"bold"} c={colors["blue-button"]}>Total Responden</Text>
|
justify="space-between"
|
||||||
<Text ta={"end"} fz={"h1"} fw={"bold"} c={colors["blue-button"]}>
|
align={{ base: "flex-start", sm: "center" }}
|
||||||
|
gap={{ base: "xs", sm: "md" }}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
fw={700}
|
||||||
|
ta={{ base: "center", sm: "left" }}
|
||||||
|
fz={{ base: "0.95rem", sm: "1rem" }}
|
||||||
|
lh={1.3}
|
||||||
|
>
|
||||||
|
Pelayanan Terhadap Publik Desa Darmasaba
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
mt={{ base: "sm", sm: 0 }}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
textAlign: 'right',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text fz={{ base: "0.8rem", sm: "0.95rem" }} fw={700} c={colors["blue-button"]} lh={1.2}>
|
||||||
|
Total Responden
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
ta="end"
|
||||||
|
fz={{ base: "1.6rem", sm: "2rem" }}
|
||||||
|
fw={800}
|
||||||
|
c={colors["blue-button"]}
|
||||||
|
lh={1.02}
|
||||||
|
>
|
||||||
{state.findMany.total.toLocaleString('id-ID')}
|
{state.findMany.total.toLocaleString('id-ID')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
<Box style={{ overflowX: 'auto', width: '100%' }}>
|
||||||
<BarChart
|
<BarChart
|
||||||
h={window.innerWidth < 480 ? 200 : 300}
|
h={300}
|
||||||
data={barChartData}
|
data={barChartData}
|
||||||
dataKey="month"
|
dataKey="month"
|
||||||
series={[{ name: 'Responden', color: colors['blue-button'] }]}
|
series={[{ name: 'Responden', color: colors['blue-button'] }]}
|
||||||
@@ -198,23 +245,25 @@ function Kepuasan() {
|
|||||||
yAxisLabel="Jumlah Responden"
|
yAxisLabel="Jumlah Responden"
|
||||||
withTooltip
|
withTooltip
|
||||||
tooltipAnimationDuration={200}
|
tooltipAnimationDuration={200}
|
||||||
|
xAxisProps={{
|
||||||
|
angle: -45,
|
||||||
|
textAnchor: 'end',
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
style={{ minWidth: 'fit-content' }}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Box py={"xl"}>
|
|
||||||
<SimpleGrid
|
<Box py="xl">
|
||||||
cols={{ base: 1, sm: 2, lg: 3 }}
|
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md" verticalSpacing="md">
|
||||||
spacing="md"
|
|
||||||
verticalSpacing="md"
|
|
||||||
>
|
|
||||||
{/* Chart Jenis Kelamin */}
|
{/* Chart Jenis Kelamin */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Jenis Kelamin</Title>
|
<Title order={4} fz={{ base: "1rem", md: "1.1rem" }} lh={1.2}>Jenis Kelamin</Title>
|
||||||
{donutDataJenisKelamin.every(item => item.value === 0) ? (
|
{donutDataJenisKelamin.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz="sm">Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
|
||||||
</Text>
|
|
||||||
) : (
|
) : (
|
||||||
<Paper p="md" radius="md" withBorder>
|
<Paper p="md" radius="md" withBorder>
|
||||||
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||||
@@ -224,19 +273,20 @@ function Kepuasan() {
|
|||||||
withTooltip
|
withTooltip
|
||||||
tooltipAnimationDuration={200}
|
tooltipAnimationDuration={200}
|
||||||
withLabels
|
withLabels
|
||||||
labelsPosition="inside" // 👈 ini yang penting!
|
labelsPosition="inside"
|
||||||
labelsType="percent"
|
labelsType="percent"
|
||||||
withLabelsLine
|
withLabelsLine
|
||||||
size={isMobile ? 180 : 250} // 👈 kecilkan ukuran di mobile
|
size={isMobile ? 180 : 250}
|
||||||
data={donutDataJenisKelamin}
|
data={donutDataJenisKelamin}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Stack gap="sm" mt="md">
|
<Stack gap="sm" mt="md">
|
||||||
{donutDataJenisKelamin.map((entry) => (
|
{donutDataJenisKelamin.map((entry) => (
|
||||||
<Flex key={entry.name} gap="md" align="center">
|
<Flex key={entry.name} gap="md" align="center">
|
||||||
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
||||||
<Text size="sm">{entry.name}: {entry.value}</Text>
|
<Text fz="sm" lh={1.25}>{entry.name}: {entry.value}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -249,11 +299,9 @@ function Kepuasan() {
|
|||||||
{/* Chart Rating */}
|
{/* Chart Rating */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Ulasan</Title>
|
<Title order={4} fz={{ base: "1rem", md: "1.1rem" }} lh={1.2}>Ulasan</Title>
|
||||||
{donutDataRating.every(item => item.value === 0) ? (
|
{donutDataRating.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz="sm">Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
|
||||||
</Text>
|
|
||||||
) : (
|
) : (
|
||||||
<Paper p="md" radius="md" withBorder>
|
<Paper p="md" radius="md" withBorder>
|
||||||
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||||
@@ -263,20 +311,21 @@ function Kepuasan() {
|
|||||||
withTooltip
|
withTooltip
|
||||||
tooltipAnimationDuration={200}
|
tooltipAnimationDuration={200}
|
||||||
withLabels
|
withLabels
|
||||||
labelsPosition="inside" // 👈 ini yang penting!
|
labelsPosition="inside"
|
||||||
labelsType="percent"
|
labelsType="percent"
|
||||||
withLabelsLine
|
withLabelsLine
|
||||||
size={isMobile ? 180 : 250} // 👈 kecilkan ukuran di mobile
|
size={isMobile ? 180 : 250}
|
||||||
data={donutDataRating}
|
data={donutDataRating}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box mt="md" style={{ width: '100%' }}>
|
<Box mt="md" style={{ width: '100%' }}>
|
||||||
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
||||||
{donutDataRating.map((entry) => (
|
{donutDataRating.map((entry) => (
|
||||||
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
||||||
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
||||||
<Text size="xs" lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
<Text fz="xs" lh={1.2} lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
{entry.name}: {entry.value}
|
{entry.name}: {entry.value}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -292,11 +341,9 @@ function Kepuasan() {
|
|||||||
{/* Chart Kelompok Umur */}
|
{/* Chart Kelompok Umur */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Umur</Title>
|
<Title order={4} fz={{ base: "1rem", md: "1.1rem" }} lh={1.2}>Umur</Title>
|
||||||
{donutDataKelompokUmur.every(item => item.value === 0) ? (
|
{donutDataKelompokUmur.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz="sm">Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
|
||||||
</Text>
|
|
||||||
) : (
|
) : (
|
||||||
<Paper p="md" radius="md" withBorder>
|
<Paper p="md" radius="md" withBorder>
|
||||||
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||||
@@ -306,20 +353,21 @@ function Kepuasan() {
|
|||||||
withTooltip
|
withTooltip
|
||||||
tooltipAnimationDuration={200}
|
tooltipAnimationDuration={200}
|
||||||
withLabels
|
withLabels
|
||||||
labelsPosition="inside"// 👈 ini yang penting!
|
labelsPosition="inside"
|
||||||
labelsType="percent"
|
labelsType="percent"
|
||||||
withLabelsLine
|
withLabelsLine
|
||||||
size={isMobile ? 180 : 250} // 👈 kecilkan ukuran di mobile
|
size={isMobile ? 180 : 250}
|
||||||
data={donutDataKelompokUmur}
|
data={donutDataKelompokUmur}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box mt="md" style={{ width: '100%' }}>
|
<Box mt="md" style={{ width: '100%' }}>
|
||||||
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
||||||
{donutDataKelompokUmur.map((entry) => (
|
{donutDataKelompokUmur.map((entry) => (
|
||||||
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
||||||
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
||||||
<Text size="xs" lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
<Text fz="xs" lh={1.2} lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
{entry.name}: {entry.value}
|
{entry.name}: {entry.value}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -331,17 +379,19 @@ function Kepuasan() {
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
<Modal opened={opened} onClose={close} title="Ajukan Responden" centered>
|
<Modal opened={opened} onClose={close} title="Ajukan Responden" centered>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors['white-1']} p="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Nama"
|
label="Nama"
|
||||||
type='text'
|
type="text"
|
||||||
placeholder="Masukkan nama"
|
placeholder="Masukkan nama"
|
||||||
value={state.create.form.name}
|
value={state.create.form.name}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
@@ -415,8 +465,9 @@ function Kepuasan() {
|
|||||||
mt={10}
|
mt={10}
|
||||||
bg={colors['blue-button']}
|
bg={colors['blue-button']}
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
|
style={{ fontWeight: 700 }}
|
||||||
>
|
>
|
||||||
Submit
|
<Text fz="sm" ta="center" c="white">Submit</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
@@ -424,72 +475,108 @@ function Kepuasan() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p={"sm"} my={"xs"}>
|
<Stack p="sm" my="xs">
|
||||||
<Container size="lg" px="sm">
|
<Container size="lg" px="sm">
|
||||||
<Center>
|
<Center>
|
||||||
<Text
|
<Title
|
||||||
|
order={2}
|
||||||
ta="center"
|
ta="center"
|
||||||
fz={{ base: '2rem', md: '2.8rem' }}
|
fz={{ base: '2rem', md: '2.8rem' }}
|
||||||
|
lh={{ base: 1.05, md: 1.04 }}
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
fw={800}
|
fw={800}
|
||||||
style={{ letterSpacing: '-0.5px' }}
|
style={{ letterSpacing: '-0.5px' }}
|
||||||
>Indeks Kepuasan Masyarakat</Text>
|
>
|
||||||
|
Indeks Kepuasan Masyarakat
|
||||||
|
</Title>
|
||||||
</Center>
|
</Center>
|
||||||
<Text fz={{ base: "1.2rem", md: "1.4rem" }} ta={"center"}>Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!</Text>
|
|
||||||
<Center mt={10}>
|
<Text fz={{ base: "1rem", md: "1.25rem" }} ta="center" c="black" lh={1.5} mt="sm">
|
||||||
<Button radius={"lg"} bg={colors["blue-button"]} onClick={open}>Ajukan Responden</Button>
|
Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Center mt={12}>
|
||||||
|
<Button radius="lg" bg={colors["blue-button"]} onClick={open} style={{ paddingLeft: 20, paddingRight: 20, fontWeight: 600 }}>
|
||||||
|
<Text fz={{ base: "0.95rem", md: "1rem" }} ta="center" c="white">Ajukan Responden</Text>
|
||||||
|
</Button>
|
||||||
</Center>
|
</Center>
|
||||||
</Container>
|
</Container>
|
||||||
<Box px={"md"}>
|
|
||||||
<Paper p={"lg"} bg={colors.Bg}>
|
<Box px="md">
|
||||||
<Paper p={"lg"}>
|
<Paper p="lg" bg={colors.Bg}>
|
||||||
<Stack gap={"xs"}>
|
<Paper p="lg">
|
||||||
|
<Stack gap="xs">
|
||||||
<Flex
|
<Flex
|
||||||
direction={{ base: "column", sm: "row" }}
|
direction={{ base: "column", sm: "row" }}
|
||||||
justify="space-between"
|
justify="space-between"
|
||||||
align={{ base: "flex-start", sm: "center" }}
|
align={{ base: "flex-start", sm: "center" }}
|
||||||
|
gap={{ base: "xs", sm: "md" }}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
fw={700}
|
||||||
|
ta={{ base: "center", sm: "left" }}
|
||||||
|
fz={{ base: "0.95rem", sm: "1rem" }}
|
||||||
|
lh={1.3}
|
||||||
>
|
>
|
||||||
<Text fw="bold" ta={{ base: "center", sm: "left" }}>
|
|
||||||
Pelayanan Terhadap Publik Desa Darmasaba
|
Pelayanan Terhadap Publik Desa Darmasaba
|
||||||
</Text>
|
</Text>
|
||||||
<Box mt={{ base: "sm", sm: 0 }}>
|
|
||||||
<Text fz={"sm"} fw={"bold"} c={colors["blue-button"]}>Total Responden</Text>
|
<Box
|
||||||
<Text ta={"end"} fz={"h1"} fw={"bold"} c={colors["blue-button"]}>
|
mt={{ base: "sm", sm: 0 }}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
textAlign: 'right',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text fz={{ base: "0.8rem", sm: "0.95rem" }} fw={700} c={colors["blue-button"]} lh={1.2}>
|
||||||
|
Total Responden
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
ta="end"
|
||||||
|
fz={{ base: "1.6rem", sm: "2rem" }}
|
||||||
|
fw={800}
|
||||||
|
c={colors["blue-button"]}
|
||||||
|
lh={1.02}
|
||||||
|
>
|
||||||
{state.findMany.total.toLocaleString('id-ID')}
|
{state.findMany.total.toLocaleString('id-ID')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
<Box style={{ overflowX: 'auto', width: '100%' }} pb={50}>
|
||||||
<BarChart
|
<BarChart
|
||||||
h={300}
|
h={300}
|
||||||
data={barChartData}
|
data={barChartData}
|
||||||
dataKey="month"
|
dataKey="month"
|
||||||
series={[{ name: 'Responden', color: colors['blue-button'] }]}
|
series={[{ name: 'Responden', color: colors['blue-button'] }]}
|
||||||
tickLine="y"
|
tickLine="y"
|
||||||
xAxisLabel="Bulan"
|
xAxisLabel=""
|
||||||
yAxisLabel="Jumlah Responden"
|
yAxisLabel="Jumlah Responden"
|
||||||
withTooltip
|
withTooltip
|
||||||
tooltipAnimationDuration={200}
|
tooltipAnimationDuration={200}
|
||||||
|
xAxisProps={{
|
||||||
|
angle: -45,
|
||||||
|
textAnchor: 'end',
|
||||||
|
fontSize: 12,
|
||||||
|
}}
|
||||||
|
style={{ minWidth: 'fit-content' }}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Box py={"xl"}>
|
|
||||||
<SimpleGrid
|
<Box py="xl">
|
||||||
cols={{
|
<SimpleGrid cols={{ base: 1, md: 1, lg: 1, xl: 3 }}>
|
||||||
base: 1,
|
|
||||||
md: 1,
|
|
||||||
lg: 1,
|
|
||||||
xl: 3
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Chart Jenis Kelamin */}
|
{/* Chart Jenis Kelamin */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Jenis Kelamin</Title>
|
<Title order={4} fz={{ base: "1rem", md: "1.1rem" }} lh={1.2}>Jenis Kelamin</Title>
|
||||||
{donutDataJenisKelamin.every(item => item.value === 0) ? (
|
{donutDataJenisKelamin.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz="sm">Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
|
||||||
</Text>
|
|
||||||
) : (
|
) : (
|
||||||
<Paper p="md" radius="md" withBorder>
|
<Paper p="md" radius="md" withBorder>
|
||||||
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||||
@@ -499,18 +586,18 @@ function Kepuasan() {
|
|||||||
withLabels
|
withLabels
|
||||||
withTooltip
|
withTooltip
|
||||||
labelsPosition="inside"
|
labelsPosition="inside"
|
||||||
|
|
||||||
labelsType="percent"
|
labelsType="percent"
|
||||||
size={200}
|
size={200}
|
||||||
data={donutDataJenisKelamin}
|
data={donutDataJenisKelamin}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Stack gap="sm" mt="md">
|
<Stack gap="sm" mt="md">
|
||||||
{donutDataJenisKelamin.map((entry) => (
|
{donutDataJenisKelamin.map((entry) => (
|
||||||
<Flex key={entry.name} gap="md" align="center">
|
<Flex key={entry.name} gap="md" align="center">
|
||||||
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={20} h={20} style={{ flexShrink: 0 }} />
|
||||||
<Text size="sm">{entry.name}: {entry.value}</Text>
|
<Text fz="sm" lh={1.25}>{entry.name}: {entry.value}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -523,11 +610,9 @@ function Kepuasan() {
|
|||||||
{/* Chart Rating */}
|
{/* Chart Rating */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Ulasan</Title>
|
<Title order={4} fz={{ base: "1rem", md: "1.1rem" }} lh={1.2}>Ulasan</Title>
|
||||||
{donutDataRating.every(item => item.value === 0) ? (
|
{donutDataRating.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz="sm">Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
|
||||||
</Text>
|
|
||||||
) : (
|
) : (
|
||||||
<Paper p="md" radius="md" withBorder>
|
<Paper p="md" radius="md" withBorder>
|
||||||
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||||
@@ -537,7 +622,6 @@ function Kepuasan() {
|
|||||||
withTooltip
|
withTooltip
|
||||||
tooltipAnimationDuration={200}
|
tooltipAnimationDuration={200}
|
||||||
withLabels
|
withLabels
|
||||||
|
|
||||||
labelsPosition="inside"
|
labelsPosition="inside"
|
||||||
labelsType="percent"
|
labelsType="percent"
|
||||||
withLabelsLine
|
withLabelsLine
|
||||||
@@ -546,12 +630,13 @@ function Kepuasan() {
|
|||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box mt="md" style={{ width: '100%' }}>
|
<Box mt="md" style={{ width: '100%' }}>
|
||||||
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
||||||
{donutDataRating.map((entry) => (
|
{donutDataRating.map((entry) => (
|
||||||
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
||||||
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
||||||
<Text size="xs" lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
<Text fz="xs" lh={1.2} lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
{entry.name}: {entry.value}
|
{entry.name}: {entry.value}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -567,11 +652,9 @@ function Kepuasan() {
|
|||||||
{/* Chart Kelompok Umur */}
|
{/* Chart Kelompok Umur */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Umur</Title>
|
<Title order={4} fz={{ base: "1rem", md: "1.1rem" }} lh={1.2}>Umur</Title>
|
||||||
{donutDataKelompokUmur.every(item => item.value === 0) ? (
|
{donutDataKelompokUmur.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md" fz="sm">Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
|
||||||
</Text>
|
|
||||||
) : (
|
) : (
|
||||||
<Paper p="md" radius="md" withBorder>
|
<Paper p="md" radius="md" withBorder>
|
||||||
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
<Box style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||||
@@ -581,7 +664,6 @@ function Kepuasan() {
|
|||||||
withTooltip
|
withTooltip
|
||||||
tooltipAnimationDuration={200}
|
tooltipAnimationDuration={200}
|
||||||
withLabels
|
withLabels
|
||||||
|
|
||||||
labelsPosition="inside"
|
labelsPosition="inside"
|
||||||
labelsType="percent"
|
labelsType="percent"
|
||||||
withLabelsLine
|
withLabelsLine
|
||||||
@@ -590,12 +672,13 @@ function Kepuasan() {
|
|||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box mt="md" style={{ width: '100%' }}>
|
<Box mt="md" style={{ width: '100%' }}>
|
||||||
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
||||||
{donutDataKelompokUmur.map((entry) => (
|
{donutDataKelompokUmur.map((entry) => (
|
||||||
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
<Flex key={entry.name} gap="sm" align="center" style={{ overflow: 'hidden' }}>
|
||||||
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
<Box bg={entry.color} w={16} h={16} style={{ flexShrink: 0 }} />
|
||||||
<Text size="xs" lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
<Text fz="xs" lh={1.2} lineClamp={1} style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
{entry.name}: {entry.value}
|
{entry.name}: {entry.value}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -607,13 +690,15 @@ function Kepuasan() {
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
<Modal opened={opened} onClose={close} title="Ajukan Responden" centered>
|
<Modal opened={opened} onClose={close} title="Ajukan Responden" centered>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors['white-1']} p="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Nama"
|
label="Nama"
|
||||||
@@ -691,8 +776,9 @@ function Kepuasan() {
|
|||||||
mt={10}
|
mt={10}
|
||||||
bg={colors['blue-button']}
|
bg={colors['blue-button']}
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
|
style={{ fontWeight: 700 }}
|
||||||
>
|
>
|
||||||
Submit
|
<Text fz="sm" ta="center" c="white">Submit</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -53,14 +53,23 @@ function ModuleItem({ data }: { data: ProgramInovasiItem }) {
|
|||||||
) : (
|
) : (
|
||||||
<Stack align="center" gap="xs">
|
<Stack align="center" gap="xs">
|
||||||
<IconPhotoOff size={38} stroke={1.5} />
|
<IconPhotoOff size={38} stroke={1.5} />
|
||||||
<Text size="sm" c="dimmed">
|
|
||||||
|
{/* ❗ Caption konsisten */}
|
||||||
|
<Text fz={{ base: 13, md: 14 }} c="dimmed">
|
||||||
Belum ada gambar
|
Belum ada gambar
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Center>
|
</Center>
|
||||||
|
|
||||||
<Box mt="md">
|
<Box mt="md">
|
||||||
<Text fw={600} ta="center" size="md">
|
{/* ❗ Responsive Title */}
|
||||||
|
<Text
|
||||||
|
fw={600}
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: 16, md: 18 }} // mobile → desktop
|
||||||
|
lh={1.3}
|
||||||
|
>
|
||||||
{data.name}
|
{data.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -91,10 +100,14 @@ function ModuleView() {
|
|||||||
<Center h={320}>
|
<Center h={320}>
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<IconPhotoOff size={54} stroke={1.5} />
|
<IconPhotoOff size={54} stroke={1.5} />
|
||||||
<Text size="lg" fw={600}>
|
|
||||||
|
{/* ❗ Empty title lebih besar */}
|
||||||
|
<Text fw={600} fz={{ base: 18, md: 22 }}>
|
||||||
Belum ada program inovasi
|
Belum ada program inovasi
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" c="dimmed">
|
|
||||||
|
{/* ❗ Deskripsi kecil & lembut */}
|
||||||
|
<Text fz={{ base: 14, md: 16 }} c="dimmed" ta="center" lh={1.4}>
|
||||||
Tambahkan program inovasi untuk ditampilkan di sini
|
Tambahkan program inovasi untuk ditampilkan di sini
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -103,11 +116,12 @@ function ModuleView() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea h={280} // ✅ tinggi fixed, bisa disesuaikan
|
<ScrollArea
|
||||||
|
h={280}
|
||||||
scrollbarSize={2}
|
scrollbarSize={2}
|
||||||
offsetScrollbars
|
offsetScrollbars
|
||||||
styles={{
|
styles={{
|
||||||
viewport: { paddingRight: 8 }, // kasih jarak biar scroll nggak dempet
|
viewport: { paddingRight: 8 },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg" mt="lg">
|
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg" mt="lg">
|
||||||
|
|||||||
@@ -13,10 +13,23 @@ export default function ProfileView({ data }: ProfileViewProps) {
|
|||||||
<Card radius="2xl" className="glass3" py="xl" px="lg" withBorder>
|
<Card radius="2xl" className="glass3" py="xl" px="lg" withBorder>
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<IconUserCircle size={72} stroke={1.4} />
|
<IconUserCircle size={72} stroke={1.4} />
|
||||||
<Text fw={500} c="dimmed">
|
|
||||||
|
{/* TITLE EMPTY */}
|
||||||
|
<Text
|
||||||
|
fw={600}
|
||||||
|
c="dimmed"
|
||||||
|
fz={{ base: 'lg', sm: 'xl', md: 'xl' }}
|
||||||
|
ta="center"
|
||||||
|
>
|
||||||
Profil belum tersedia
|
Profil belum tersedia
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="sm" c="dimmed">
|
|
||||||
|
{/* DESCRIPTION EMPTY */}
|
||||||
|
<Text
|
||||||
|
fz={{ base: 'sm', sm: 'md' }}
|
||||||
|
c="dimmed"
|
||||||
|
ta="center"
|
||||||
|
>
|
||||||
Data pejabat desa akan muncul di sini
|
Data pejabat desa akan muncul di sini
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -30,12 +43,12 @@ export default function ProfileView({ data }: ProfileViewProps) {
|
|||||||
align="end"
|
align="end"
|
||||||
pos="relative"
|
pos="relative"
|
||||||
w={{
|
w={{
|
||||||
base: '100%', // mobile: full width
|
base: '100%',
|
||||||
xs: '100%', // small mobile
|
xs: '100%',
|
||||||
sm: '85%', // tablet: 85%
|
sm: '85%',
|
||||||
md: '60%', // laptop: 60%
|
md: '60%',
|
||||||
lg: '55%', // laptop large: 55%
|
lg: '55%',
|
||||||
xl: '50%' // extra large (4K): 50%
|
xl: '50%',
|
||||||
}}
|
}}
|
||||||
px={{ base: 'md', sm: 'lg', md: 'xl', xl: '2xl' }}
|
px={{ base: 'md', sm: 'lg', md: 'xl', xl: '2xl' }}
|
||||||
h={{ base: 'auto', sm: '500px', md: '600px', lg: '650px', xl: '700px' }}
|
h={{ base: 'auto', sm: '500px', md: '600px', lg: '650px', xl: '700px' }}
|
||||||
@@ -67,13 +80,17 @@ export default function ProfileView({ data }: ProfileViewProps) {
|
|||||||
) : (
|
) : (
|
||||||
<Stack align="center" gap="xs" w="100%" py="xl">
|
<Stack align="center" gap="xs" w="100%" py="xl">
|
||||||
<IconUserCircle size={96} stroke={1.5} />
|
<IconUserCircle size={96} stroke={1.5} />
|
||||||
<Text c="dimmed" fz="sm">
|
<Text
|
||||||
|
c="dimmed"
|
||||||
|
fz={{ base: 'sm', sm: 'md' }}
|
||||||
|
ta="center"
|
||||||
|
>
|
||||||
Belum ada foto
|
Belum ada foto
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Box nama dan jabatan - responsive positioning */}
|
{/* Box nama & jabatan */}
|
||||||
<Box
|
<Box
|
||||||
pos="absolute"
|
pos="absolute"
|
||||||
bottom={{ base: -30, sm: -25, md: -20 }}
|
bottom={{ base: -30, sm: -25, md: -20 }}
|
||||||
@@ -94,17 +111,21 @@ export default function ProfileView({ data }: ProfileViewProps) {
|
|||||||
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
{/* POSITION / JABATAN */}
|
||||||
<Text
|
<Text
|
||||||
fz={{ base: 'xs', sm: 'sm' }}
|
fz={{ base: 'xs', sm: 'sm', md: 'md' }}
|
||||||
c="dimmed"
|
c="dimmed"
|
||||||
lineClamp={1}
|
lineClamp={1}
|
||||||
>
|
>
|
||||||
{data.position || 'Tidak ada jabatan'}
|
{data.position || 'Tidak ada jabatan'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{/* NAME */}
|
||||||
<Text
|
<Text
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
fw={700}
|
fw={700}
|
||||||
fz={{ base: 'lg', sm: 'xl' }}
|
fz={{ base: 'lg', sm: 'xl', md: 'xl', lg: '2xl' }}
|
||||||
mt={4}
|
mt={4}
|
||||||
lineClamp={2}
|
lineClamp={2}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ function SosmedView({
|
|||||||
data.map((item, k) => (
|
data.map((item, k) => (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
key={k}
|
key={k}
|
||||||
label={item.name || "Tautan Sosial"}
|
label={
|
||||||
|
<Text fz={{ base: 12, md: 14 }}>
|
||||||
|
{item.name || "Tautan Sosial"}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
withArrow
|
withArrow
|
||||||
position="top"
|
position="top"
|
||||||
transitionProps={{ transition: "pop", duration: 150 }}
|
transitionProps={{ transition: "pop", duration: 150 }}
|
||||||
@@ -57,7 +61,7 @@ function SosmedView({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Box bg={colors['blue-button']} w="100%" h="100%" />;
|
return <Box bg={colors["blue-button"]} w="100%" h="100%" />;
|
||||||
})()}
|
})()}
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -72,7 +76,12 @@ function SosmedView({
|
|||||||
background: "linear-gradient(135deg, #1C6EA4 0%, #000 100%)",
|
background: "linear-gradient(135deg, #1C6EA4 0%, #000 100%)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text ta="center" c="dimmed" size="sm">
|
<Text
|
||||||
|
ta="center"
|
||||||
|
c="dimmed"
|
||||||
|
fz={{ base: 13, md: 15 }}
|
||||||
|
lh={1.4}
|
||||||
|
>
|
||||||
Belum ada media sosial yang terhubung
|
Belum ada media sosial yang terhubung
|
||||||
</Text>
|
</Text>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const getWorkStatus = (day: string, currentTime: string): { status: string; mess
|
|||||||
: { status: "Tutup", message: "08:00 - 17:00" };
|
: { status: "Tutup", message: "08:00 - 17:00" };
|
||||||
};
|
};
|
||||||
|
|
||||||
// Skeleton component untuk Social Media
|
// 🟦 Skeleton component untuk Social Media
|
||||||
const SosmedSkeleton = () => (
|
const SosmedSkeleton = () => (
|
||||||
<Flex gap="md" justify="center" wrap="wrap">
|
<Flex gap="md" justify="center" wrap="wrap">
|
||||||
{[1, 2, 3, 4].map((i) => (
|
{[1, 2, 3, 4].map((i) => (
|
||||||
@@ -68,7 +68,7 @@ const SosmedSkeleton = () => (
|
|||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Skeleton component untuk Profile
|
// 🟦 Skeleton component untuk Profile
|
||||||
const ProfileSkeleton = () => (
|
const ProfileSkeleton = () => (
|
||||||
<Card
|
<Card
|
||||||
radius="xl"
|
radius="xl"
|
||||||
@@ -158,6 +158,8 @@ function LandingPage() {
|
|||||||
<Stack w={{ base: "100%", md: "65%" }} gap="lg">
|
<Stack w={{ base: "100%", md: "65%" }} gap="lg">
|
||||||
<Card radius="xl" bg={colors.grey[1]} p="lg" mt={10} shadow="xl">
|
<Card radius="xl" bg={colors.grey[1]} p="lg" mt={10} shadow="xl">
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
|
|
||||||
|
{/* Header Logo */}
|
||||||
<Flex gap="md" wrap="wrap">
|
<Flex gap="md" wrap="wrap">
|
||||||
<Group>
|
<Group>
|
||||||
<Box bg="white" w={72} h={72} p="sm" style={{ borderRadius: 24 }}>
|
<Box bg="white" w={72} h={72} p="sm" style={{ borderRadius: 24 }}>
|
||||||
@@ -167,6 +169,8 @@ function LandingPage() {
|
|||||||
<Image loading="lazy" src="/pudak-icon.png" alt="Logo Pudak" fit="contain" />
|
<Image loading="lazy" src="/pudak-icon.png" alt="Logo Pudak" fit="contain" />
|
||||||
</Box>
|
</Box>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
{/* Jam Operasional */}
|
||||||
<Grid w="100%">
|
<Grid w="100%">
|
||||||
<Grid.Col span={12}>
|
<Grid.Col span={12}>
|
||||||
<Paper
|
<Paper
|
||||||
@@ -177,36 +181,58 @@ function LandingPage() {
|
|||||||
style={{ position: "relative", overflow: "hidden" }}
|
style={{ position: "relative", overflow: "hidden" }}
|
||||||
>
|
>
|
||||||
<Grid gutter="md">
|
<Grid gutter="md">
|
||||||
|
|
||||||
|
{/* Kolom 1 */}
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }}>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Flex align="center" gap="xs">
|
<Flex align="center" gap="xs">
|
||||||
<IconCalendarTime size={16} color="white" />
|
<IconCalendarTime size={16} color="white" />
|
||||||
<Text c="white" fz="sm">Jam Operasional</Text>
|
<Text c="white" fz={{ base: "xs", md: "sm" }}>
|
||||||
|
Jam Operasional
|
||||||
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Paper p="sm" radius="md" bg="white">
|
<Paper p="sm" radius="md" bg="white">
|
||||||
<Tooltip label="Status saat ini berdasarkan jam operasional kantor">
|
<Tooltip label="Status saat ini berdasarkan jam operasional kantor">
|
||||||
<Badge
|
<Badge
|
||||||
color={workStatus.status === "Buka" ? "green" : "red"}
|
color={workStatus.status === "Buka" ? "green" : "red"}
|
||||||
radius="sm"
|
radius="sm"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
|
size="md"
|
||||||
>
|
>
|
||||||
{workStatus.status}
|
{workStatus.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Text fw="bold" fz="lg">{workStatus.message}</Text>
|
|
||||||
|
<Text
|
||||||
|
fw={700}
|
||||||
|
fz={{ base: "md", md: "lg" }}
|
||||||
|
mt={4}
|
||||||
|
>
|
||||||
|
{workStatus.message}
|
||||||
|
</Text>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
|
|
||||||
|
{/* Kolom 2 */}
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }}>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Flex align="center" gap="xs">
|
<Flex align="center" gap="xs">
|
||||||
<IconInfoCircle size={16} color="white" />
|
<IconInfoCircle size={16} color="white" />
|
||||||
<Text c="white" fz="sm">Hari Ini</Text>
|
<Text c="white" fz={{ base: "xs", md: "sm" }}>
|
||||||
|
Hari Ini
|
||||||
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Paper p="sm" radius="md" bg="white">
|
<Paper p="sm" radius="md" bg="white">
|
||||||
<Text fz="sm">Status Kantor</Text>
|
<Text fz={{ base: "xs", md: "sm" }} c="dimmed">
|
||||||
<Text fw="bold" fz="lg">
|
Status Kantor
|
||||||
{workStatus.status === "Buka" ? "Sedang Beroperasi" : "Tidak Beroperasi"}
|
</Text>
|
||||||
|
<Text fw={700} fz={{ base: "md", md: "lg" }}>
|
||||||
|
{workStatus.status === "Buka"
|
||||||
|
? "Sedang Beroperasi"
|
||||||
|
: "Tidak Beroperasi"}
|
||||||
</Text>
|
</Text>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -217,19 +243,29 @@ function LandingPage() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
{/* MODULE VIEW */}
|
||||||
<ModuleView />
|
<ModuleView />
|
||||||
|
|
||||||
|
{/* Sosmed */}
|
||||||
{isLoadingSosmed ? (
|
{isLoadingSosmed ? (
|
||||||
<SosmedSkeleton />
|
<SosmedSkeleton />
|
||||||
) : socialMedia.length > 0 ? (
|
) : socialMedia.length > 0 ? (
|
||||||
<SosmedView data={socialMedia} />
|
<SosmedView data={socialMedia} />
|
||||||
) : (
|
) : (
|
||||||
<Center>
|
<Center>
|
||||||
<Text c="dimmed">Belum ada tautan media sosial yang tersedia</Text>
|
<Text fz={{ base: "sm", md: "md" }} c="dimmed">
|
||||||
|
Belum ada tautan media sosial yang tersedia
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Text ta="center" c={colors.trans.dark[2]}>
|
{/* CTA Text */}
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
c={colors.trans.dark[2]}
|
||||||
|
fz={{ base: "sm", md: "md" }}
|
||||||
|
lh={1.5}
|
||||||
|
>
|
||||||
Bagikan ide, kritik, atau saran Anda untuk mendukung pembangunan desa.
|
Bagikan ide, kritik, atau saran Anda untuk mendukung pembangunan desa.
|
||||||
Semua lebih mudah dengan fitur interaktif yang kami sediakan.
|
Semua lebih mudah dengan fitur interaktif yang kami sediakan.
|
||||||
</Text>
|
</Text>
|
||||||
@@ -237,6 +273,7 @@ function LandingPage() {
|
|||||||
</Card>
|
</Card>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
{/* PROFIL */}
|
||||||
{isLoadingProfile ? (
|
{isLoadingProfile ? (
|
||||||
<ProfileSkeleton />
|
<ProfileSkeleton />
|
||||||
) : profile ? (
|
) : profile ? (
|
||||||
@@ -251,7 +288,9 @@ function LandingPage() {
|
|||||||
style={{ height: "fit-content" }}
|
style={{ height: "fit-content" }}
|
||||||
>
|
>
|
||||||
<Center h={300}>
|
<Center h={300}>
|
||||||
<Text c="dimmed">Informasi profil belum tersedia</Text>
|
<Text fz={{ base: "sm", md: "md" }} c="dimmed">
|
||||||
|
Informasi profil belum tersedia
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -28,20 +28,41 @@ const textHeading = {
|
|||||||
const HEIGHT = 720;
|
const HEIGHT = 720;
|
||||||
|
|
||||||
function Layanan() {
|
function Layanan() {
|
||||||
|
// responsive breakpoints: base = mobile, md = desktop/tablet landscape
|
||||||
return (
|
return (
|
||||||
<Stack pos="relative" bg={colors.grey[1]} gap="xl" py="md">
|
<Stack pos="relative" bg={colors.grey[1]} gap="xl" py="md">
|
||||||
<Container w={{ base: "100%", md: "80%" }} p="md">
|
<Container w={{ base: "100%", md: "80%" }} p="md">
|
||||||
<Stack align="center" gap="0">
|
<Stack align="center" gap="0">
|
||||||
|
{/* Main title - semantic h1 */}
|
||||||
<Text
|
<Text
|
||||||
fw="bold"
|
component="h1"
|
||||||
|
fw={700}
|
||||||
c={colors["blue-button"]}
|
c={colors["blue-button"]}
|
||||||
fz={{ base: "1.8rem", md: "3.4rem" }}
|
ta="center"
|
||||||
|
// responsive sizes: mobile ~28px, desktop ~48px
|
||||||
|
fz={{ base: "1.75rem", md: "3rem" }}
|
||||||
|
// tighter line-height for large headings, slightly more compact on desktop
|
||||||
|
style={{ lineHeight: "1.05" }}
|
||||||
>
|
>
|
||||||
{textHeading.title}
|
{textHeading.title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text ta="center" fz={{ base: "1rem", md: "1.3rem" }}>
|
|
||||||
|
{/* Description - readable line-height and constrained width on desktop */}
|
||||||
|
<Text
|
||||||
|
component="p"
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: "0.95rem", md: "1.15rem" }}
|
||||||
|
// more comfortable line-height for paragraphs
|
||||||
|
style={{
|
||||||
|
lineHeight: "1.6",
|
||||||
|
maxWidth: "70ch",
|
||||||
|
marginTop: 8,
|
||||||
|
}}
|
||||||
|
c="black"
|
||||||
|
>
|
||||||
{textHeading.des}
|
{textHeading.des}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Box p="md">
|
<Box p="md">
|
||||||
<Button
|
<Button
|
||||||
component={Link}
|
component={Link}
|
||||||
@@ -49,6 +70,14 @@ function Layanan() {
|
|||||||
variant="filled"
|
variant="filled"
|
||||||
bg={colors["blue-button"]}
|
bg={colors["blue-button"]}
|
||||||
radius={100}
|
radius={100}
|
||||||
|
// accessible sizing: slightly smaller on mobile, comfortable on desktop
|
||||||
|
style={{
|
||||||
|
paddingLeft: 20,
|
||||||
|
paddingRight: 20,
|
||||||
|
fontSize: "md",
|
||||||
|
// ensure button text doesn't overflow on very narrow screens
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Detail
|
Detail
|
||||||
</Button>
|
</Button>
|
||||||
@@ -175,7 +204,7 @@ function Slider() {
|
|||||||
startXRef.current = e.pageX - containerRef.current.offsetLeft;
|
startXRef.current = e.pageX - containerRef.current.offsetLeft;
|
||||||
scrollLeftRef.current = containerRef.current.scrollLeft;
|
scrollLeftRef.current = containerRef.current.scrollLeft;
|
||||||
velocityRef.current = 0;
|
velocityRef.current = 0;
|
||||||
containerRef.current.style.cursor = 'grabbing';
|
containerRef.current.style.cursor = "grabbing";
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseMove = (e: React.MouseEvent) => {
|
const handleMouseMove = (e: React.MouseEvent) => {
|
||||||
@@ -196,7 +225,7 @@ function Slider() {
|
|||||||
if (!containerRef.current || mobile) return;
|
if (!containerRef.current || mobile) return;
|
||||||
|
|
||||||
isDraggingRef.current = false;
|
isDraggingRef.current = false;
|
||||||
containerRef.current.style.cursor = 'grab';
|
containerRef.current.style.cursor = "grab";
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleWheel = (e: React.WheelEvent) => {
|
const handleWheel = (e: React.WheelEvent) => {
|
||||||
@@ -215,7 +244,7 @@ function Slider() {
|
|||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Text ta="center" c="dimmed">
|
<Text ta="center" c="dimmed" fz={{ base: "0.95rem", md: "1rem" }}>
|
||||||
Tidak ada layanan tersedia
|
Tidak ada layanan tersedia
|
||||||
</Text>
|
</Text>
|
||||||
</Container>
|
</Container>
|
||||||
@@ -240,6 +269,8 @@ function Slider() {
|
|||||||
scrollbarWidth: "none",
|
scrollbarWidth: "none",
|
||||||
msOverflowStyle: "none",
|
msOverflowStyle: "none",
|
||||||
}}
|
}}
|
||||||
|
// ensure keyboard accessibility: allow focus outline when focused
|
||||||
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
@@ -287,26 +318,56 @@ function Slider() {
|
|||||||
pos="relative"
|
pos="relative"
|
||||||
>
|
>
|
||||||
<Box p="lg">
|
<Box p="lg">
|
||||||
|
{/* slide title - semantic h2 */}
|
||||||
<Text
|
<Text
|
||||||
fw="bold"
|
component="h2"
|
||||||
|
fw={700}
|
||||||
c="white"
|
c="white"
|
||||||
fz={{base: "xl", md: "3.5rem"}}
|
fz={{ base: "1.25rem", md: "2.4rem" }}
|
||||||
|
// tighter heading line-height but ensure readability on mobile
|
||||||
style={{
|
style={{
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
|
lineHeight: mobile ? "1.15" : "1.02",
|
||||||
|
// clamp long names visually
|
||||||
|
display: "-webkit-box",
|
||||||
|
WebkitLineClamp: 2,
|
||||||
|
WebkitBoxOrient: "vertical",
|
||||||
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
|
title={_.startCase(item.name)}
|
||||||
>
|
>
|
||||||
{_.startCase(item.name)}
|
{_.startCase(item.name)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{/* optional short description - rendered if exists */}
|
||||||
|
{item.description ? (
|
||||||
|
<Text
|
||||||
|
component="p"
|
||||||
|
mt="sm"
|
||||||
|
c="white"
|
||||||
|
fz={{ base: "0.9rem", md: "1rem" }}
|
||||||
|
style={{ lineHeight: "1.5", textAlign: "center" }}
|
||||||
|
>
|
||||||
|
{item.description}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
</Box>
|
</Box>
|
||||||
<Group justify="center">
|
|
||||||
|
<Group justify="center" mb="lg">
|
||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push(`/darmasaba/desa/layanan/${item.id}`)
|
router.push(`/darmasaba/desa/layanan/${item.id}`)
|
||||||
}
|
}
|
||||||
px={46}
|
px={mobile ? 20 : 46}
|
||||||
radius="100"
|
radius="100"
|
||||||
size="md"
|
size={mobile ? "sm" : "md"}
|
||||||
bg={colors["blue-button"]}
|
bg={colors["blue-button"]}
|
||||||
|
// ensure button text readable on all sizes
|
||||||
|
style={{
|
||||||
|
fontSize: mobile ? "0.95rem" : "1rem",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
}}
|
||||||
|
aria-label={`Detail layanan ${_.startCase(item.name)}`}
|
||||||
>
|
>
|
||||||
Detail
|
Detail
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,9 +1,21 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import penghargaanState from "@/app/admin/(dashboard)/_state/desa/penghargaan";
|
import penghargaanState from "@/app/admin/(dashboard)/_state/desa/penghargaan";
|
||||||
import { Stack, Box, Container, Button, Text, Loader, Paper, Center, ActionIcon } from "@mantine/core";
|
import {
|
||||||
|
Stack,
|
||||||
|
Box,
|
||||||
|
Container,
|
||||||
|
Button,
|
||||||
|
Text,
|
||||||
|
Loader,
|
||||||
|
Paper,
|
||||||
|
Center,
|
||||||
|
ActionIcon,
|
||||||
|
Title,
|
||||||
|
} from "@mantine/core";
|
||||||
import { IconAward, IconArrowRight, IconPlayerPlay } from "@tabler/icons-react";
|
import { IconAward, IconArrowRight, IconPlayerPlay } from "@tabler/icons-react";
|
||||||
import { useTransitionRouter } from 'next-view-transitions';
|
import { useTransitionRouter } from "next-view-transitions";
|
||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
import { useProxy } from "valtio/utils";
|
import { useProxy } from "valtio/utils";
|
||||||
import { useMediaQuery } from "@mantine/hooks";
|
import { useMediaQuery } from "@mantine/hooks";
|
||||||
@@ -12,43 +24,33 @@ function Penghargaan() {
|
|||||||
const router = useTransitionRouter();
|
const router = useTransitionRouter();
|
||||||
const state = useProxy(penghargaanState);
|
const state = useProxy(penghargaanState);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
const isMobile = useMediaQuery("(max-width: 768px)");
|
||||||
const [isVideoLoaded, setIsVideoLoaded] = useState(false);
|
const [isVideoLoaded, setIsVideoLoaded] = useState(false);
|
||||||
const [showVideo, setShowVideo] = useState(true);
|
|
||||||
const [videoError, setVideoError] = useState(false);
|
const [videoError, setVideoError] = useState(false);
|
||||||
|
const [showPlayButton, setShowPlayButton] = useState(false);
|
||||||
const videoRef = useRef<HTMLVideoElement>(null);
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
|
const hasTriedAutoplay = useRef(false);
|
||||||
|
|
||||||
// Deteksi iOS dengan lebih akurat
|
// ---- TYPOGRAPHY SCALE (RESPONSIVE) ----
|
||||||
const isIOS = typeof window !== 'undefined' && (
|
// ukuran dalam px, lh = line-height
|
||||||
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
const TYPO = {
|
||||||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) // iPad dengan iPadOS 13+
|
// utama / hero title
|
||||||
);
|
title: { base: 22, md: 36, lh: 1.08 }, // lebih menonjol di desktop
|
||||||
|
// subheading / loader / tagline
|
||||||
useEffect(() => {
|
subtitle: { base: 14, md: 16, lh: 1.35 },
|
||||||
// Di iOS, coba autoplay dulu, kalau gagal tampilkan fallback
|
// teks body / deskripsi umum
|
||||||
if (isIOS && videoRef.current) {
|
body: { base: 14, md: 16, lh: 1.6 },
|
||||||
const playPromise = videoRef.current.play();
|
// caption / small notes
|
||||||
|
small: { base: 12, md: 13, lh: 1.4 },
|
||||||
if (playPromise !== undefined) {
|
// judul dalam kartu (card title)
|
||||||
playPromise
|
paperTitle: { base: 15, md: 18, lh: 1.25 },
|
||||||
.then(() => {
|
};
|
||||||
// Autoplay berhasil
|
|
||||||
setShowVideo(true);
|
|
||||||
setIsVideoLoaded(true);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// Autoplay gagal, tampilkan fallback
|
|
||||||
setShowVideo(false);
|
|
||||||
setVideoError(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [isIOS]);
|
|
||||||
|
|
||||||
|
// Load data
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
try {
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
try {
|
||||||
await state.findMany.load();
|
await state.findMany.load();
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -57,99 +59,134 @@ function Penghargaan() {
|
|||||||
loadData();
|
loadData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handlePlayVideo = () => {
|
// Attempt autoplay setelah video loaded
|
||||||
setShowVideo(true);
|
useEffect(() => {
|
||||||
setVideoError(false);
|
if (isVideoLoaded && videoRef.current && !hasTriedAutoplay.current) {
|
||||||
|
hasTriedAutoplay.current = true;
|
||||||
|
|
||||||
// Paksa play video setelah user interaction
|
const attemptAutoplay = async () => {
|
||||||
setTimeout(() => {
|
try {
|
||||||
if (videoRef.current) {
|
// Pastikan video muted sebelum play
|
||||||
videoRef.current.play().catch(err => {
|
videoRef.current!.muted = true;
|
||||||
console.error("Video play error:", err);
|
await videoRef.current!.play();
|
||||||
setVideoError(true);
|
setShowPlayButton(false);
|
||||||
});
|
console.log("✅ Autoplay berhasil");
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("⚠️ Autoplay diblokir browser:", err);
|
||||||
|
// Tampilkan tombol play jika autoplay gagal
|
||||||
|
setShowPlayButton(true);
|
||||||
}
|
}
|
||||||
}, 100);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// kalau mobile ambil 1 data aja, kalau desktop ambil 3
|
// Delay sedikit untuk memastikan video siap
|
||||||
|
setTimeout(attemptAutoplay, 100);
|
||||||
|
}
|
||||||
|
}, [isVideoLoaded]);
|
||||||
|
|
||||||
|
// Handle manual play
|
||||||
|
const handlePlayVideo = async () => {
|
||||||
|
if (videoRef.current) {
|
||||||
|
try {
|
||||||
|
videoRef.current.muted = true;
|
||||||
|
await videoRef.current.play();
|
||||||
|
setShowPlayButton(false);
|
||||||
|
setVideoError(false);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("❌ Gagal memutar video:", err);
|
||||||
|
setVideoError(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ambil data terbatas berdasarkan perangkat
|
||||||
const data = state.findMany.data?.slice(0, isMobile ? 1 : 3);
|
const data = state.findMany.data?.slice(0, isMobile ? 1 : 3);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pos="relative" h="auto" mih={{ base: 500, md: 720 }} style={{ overflow: 'hidden' }}>
|
<Stack pos="relative" h="auto" mih={{ base: 500, md: 720 }} style={{ overflow: "hidden" }}>
|
||||||
{/* Video Layer */}
|
{/* Video background */}
|
||||||
{showVideo && !videoError && (
|
{!videoError && (
|
||||||
<video
|
<video
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
autoPlay
|
|
||||||
muted
|
muted
|
||||||
loop
|
loop
|
||||||
playsInline
|
playsInline
|
||||||
preload="auto"
|
preload="auto"
|
||||||
|
webkit-playsinline="true"
|
||||||
onLoadedData={() => setIsVideoLoaded(true)}
|
onLoadedData={() => setIsVideoLoaded(true)}
|
||||||
onError={() => {
|
onError={() => {
|
||||||
console.error("Video load error");
|
console.error("❌ Video gagal dimuat");
|
||||||
setVideoError(true);
|
setVideoError(true);
|
||||||
setShowVideo(false);
|
}}
|
||||||
|
onCanPlayThrough={() => {
|
||||||
|
console.log("✅ Video siap diputar");
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: "absolute",
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: '100%',
|
width: "100%",
|
||||||
height: '100%',
|
height: "100%",
|
||||||
objectFit: 'cover',
|
objectFit: "cover",
|
||||||
opacity: isVideoLoaded ? 1 : 0,
|
opacity: isVideoLoaded ? 1 : 0,
|
||||||
transition: 'opacity 0.5s ease',
|
transition: "opacity 0.5s ease",
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<source src="/assets/videos/award.mp4" type="video/mp4" />
|
<source src="/assets/videos/award.mp4" type="video/mp4" />
|
||||||
|
Browser Anda tidak mendukung video.
|
||||||
</video>
|
</video>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Fallback Image + Play Button */}
|
{/* Fallback background image */}
|
||||||
{(!showVideo || videoError) && (
|
{(videoError || !isVideoLoaded) && (
|
||||||
<Box
|
<Box
|
||||||
onClick={handlePlayVideo}
|
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: "absolute",
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: '100%',
|
width: "100%",
|
||||||
height: '100%',
|
height: "100%",
|
||||||
backgroundImage: "url('/mangupuraaward.jpeg')",
|
backgroundImage: "url('/mangupuraaward.jpeg')",
|
||||||
backgroundSize: 'cover',
|
backgroundSize: "cover",
|
||||||
backgroundPosition: 'center',
|
backgroundPosition: "center",
|
||||||
backgroundRepeat: 'no-repeat',
|
backgroundRepeat: "no-repeat",
|
||||||
cursor: 'pointer',
|
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Tombol Play (muncul jika autoplay gagal atau video error) */}
|
||||||
|
{(showPlayButton || videoError) && (
|
||||||
<Center
|
<Center
|
||||||
|
onClick={handlePlayVideo}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
position: "absolute",
|
||||||
height: '100%',
|
top: 0,
|
||||||
background: 'rgba(0,0,0,0.3)', // overlay gelap agar icon terlihat
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
cursor: "pointer",
|
||||||
|
zIndex: 2,
|
||||||
|
pointerEvents: showPlayButton || videoError ? "auto" : "none",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
size={80}
|
size={isMobile ? 64 : 80}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
color="blue"
|
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: 'rgba(255,255,255,0.9)',
|
backgroundColor: "rgba(255,255,255,0.95)",
|
||||||
boxShadow: '0 8px 32px rgba(0,0,0,0.3)',
|
boxShadow: "0 8px 32px rgba(0,0,0,0.3)",
|
||||||
|
animation: "pulse 2s infinite",
|
||||||
}}
|
}}
|
||||||
|
aria-label="Play background video"
|
||||||
>
|
>
|
||||||
<IconPlayerPlay size={40} color="var(--mantine-color-blue-6)" />
|
<IconPlayerPlay size={isMobile ? 34 : 40} color="var(--mantine-color-blue-6)" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Overlay Gradient + Content */}
|
{/* Overlay konten */}
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@@ -161,22 +198,39 @@ function Penghargaan() {
|
|||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container w={{ base: "100%", md: "80%" }} mih={{ base: 500, md: 720 }} p="xl">
|
<Container w={{ base: "100%", md: "80%" }} maw={1100} mih={{ base: 500, md: 720 }} p={{ base: "lg", md: "xl" }}>
|
||||||
<Stack justify="center" align="center" gap="xl" h="100%">
|
<Stack justify="center" align="center" gap="xl" h="100%">
|
||||||
<Text
|
{/* Hero Title - pakai Title agar semantics lebih jelas */}
|
||||||
fw={900}
|
<Title
|
||||||
fz={{ base: "2rem", md: "2.8rem" }}
|
order={2}
|
||||||
ta="center"
|
style={{
|
||||||
variant="gradient"
|
fontWeight: 800,
|
||||||
gradient={{ from: "cyan", to: "blue", deg: 60 }}
|
lineHeight: TYPO.title.lh,
|
||||||
|
// Mantine support fz prop but inline style fallback ok:
|
||||||
|
fontSize: isMobile ? TYPO.title.base : TYPO.title.md,
|
||||||
|
textAlign: "center",
|
||||||
|
// gradient via CSS text-fill technique (ke Mantine gradient prop juga bisa)
|
||||||
|
background: "-webkit-linear-gradient(60deg, #22D3EE 0%, #3B82F6 100%)",
|
||||||
|
WebkitBackgroundClip: "text",
|
||||||
|
WebkitTextFillColor: "transparent",
|
||||||
|
}}
|
||||||
|
aria-label="Penghargaan Desa"
|
||||||
>
|
>
|
||||||
Penghargaan Desa
|
Penghargaan Desa
|
||||||
</Text>
|
</Title>
|
||||||
|
|
||||||
|
{/* Content area */}
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<Loader color="blue" size="lg" />
|
<Loader color="blue" size={isMobile ? "md" : "lg"} />
|
||||||
<Text c="gray.3" fz="lg">Sedang memuat data penghargaan...</Text>
|
<Text
|
||||||
|
c="gray.3"
|
||||||
|
fz={isMobile ? TYPO.subtitle.base : TYPO.subtitle.md}
|
||||||
|
lh={TYPO.subtitle.lh}
|
||||||
|
ta="center"
|
||||||
|
>
|
||||||
|
Sedang memuat data penghargaan...
|
||||||
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
) : data && data.length > 0 ? (
|
) : data && data.length > 0 ? (
|
||||||
<Stack gap="md" w="100%" maw={600}>
|
<Stack gap="md" w="100%" maw={600}>
|
||||||
@@ -185,45 +239,96 @@ function Penghargaan() {
|
|||||||
key={k}
|
key={k}
|
||||||
withBorder
|
withBorder
|
||||||
radius="xl"
|
radius="xl"
|
||||||
p="lg"
|
p={isMobile ? "md" : "lg"}
|
||||||
shadow="xl"
|
shadow="xl"
|
||||||
style={{
|
style={{
|
||||||
background: "rgba(255,255,255,0.07)",
|
background: "rgba(255,255,255,0.07)",
|
||||||
backdropFilter: "blur(12px)",
|
backdropFilter: "blur(12px)",
|
||||||
transition: "all 0.3s ease",
|
transition: "all 0.3s ease",
|
||||||
}}
|
}}
|
||||||
|
aria-label={`Penghargaan ${v.name}`}
|
||||||
>
|
>
|
||||||
<Stack align="center" gap="xs">
|
<Stack align="center" gap="xs">
|
||||||
<IconAward size={40} color="var(--mantine-color-blue-4)" />
|
<IconAward size={isMobile ? 36 : 40} color="var(--mantine-color-blue-4)" />
|
||||||
<Text fz="lg" fw={700} c="white" ta="center">
|
<Text
|
||||||
|
// card title: lebih tegas
|
||||||
|
fz={isMobile ? TYPO.paperTitle.base : TYPO.paperTitle.md}
|
||||||
|
fw={700}
|
||||||
|
c="white"
|
||||||
|
ta="center"
|
||||||
|
lh={TYPO.paperTitle.lh}
|
||||||
|
style={{ wordBreak: "break-word" }}
|
||||||
|
title={v.name}
|
||||||
|
>
|
||||||
{v.name}
|
{v.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{/* Jika ingin menambahkan deskripsi ringkas di card, gunakan body scale */}
|
||||||
|
{v.description && (
|
||||||
|
<Text
|
||||||
|
fz={isMobile ? TYPO.body.base : TYPO.body.md}
|
||||||
|
c="gray.2"
|
||||||
|
ta="center"
|
||||||
|
lh={TYPO.body.lh}
|
||||||
|
style={{ maxWidth: 520 }}
|
||||||
|
>
|
||||||
|
{v.description}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
) : (
|
) : (
|
||||||
<Stack align="center" gap="xs">
|
<Stack align="center" gap="xs">
|
||||||
<IconAward size={48} color="var(--mantine-color-gray-5)" />
|
<IconAward size={isMobile ? 40 : 48} color="var(--mantine-color-gray-5)" />
|
||||||
<Text c="gray.4" fz="lg" ta="center">
|
<Text
|
||||||
|
c="gray.4"
|
||||||
|
fz={isMobile ? TYPO.body.base : TYPO.body.md}
|
||||||
|
ta="center"
|
||||||
|
lh={TYPO.body.lh}
|
||||||
|
>
|
||||||
Belum ada penghargaan yang tercatat
|
Belum ada penghargaan yang tercatat
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
size="lg"
|
size={isMobile ? "md" : "lg"}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
gradient={{ from: "#26667F", to: "#124170", deg: 45 }}
|
gradient={{ from: "#26667F", to: "#124170", deg: 45 }}
|
||||||
rightSection={<IconArrowRight size={20} />}
|
rightSection={<IconArrowRight size={isMobile ? 16 : 20} />}
|
||||||
onClick={() => router.push("/darmasaba/penghargaan")}
|
onClick={() => router.push("/darmasaba/penghargaan")}
|
||||||
|
aria-label="Lihat semua penghargaan"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
c="white"
|
||||||
|
fz={isMobile ? TYPO.body.base : TYPO.body.md}
|
||||||
|
fw={700}
|
||||||
|
style={{ lineHeight: TYPO.body.lh }}
|
||||||
>
|
>
|
||||||
Lihat Semua Penghargaan
|
Lihat Semua Penghargaan
|
||||||
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* CSS untuk animasi tombol play */}
|
||||||
|
<style jsx>{`
|
||||||
|
@keyframes pulse {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,31 +50,52 @@ function Potensi() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p="sm" gap="xl">
|
<Stack p="sm" gap="xl">
|
||||||
<Container w={{ base: "100%", md: "80%" }} p={"md"} >
|
{/* HEADER */}
|
||||||
<Text id="news-title" ta={"center"} fw={"bold"} c={colors["blue-button"]} fz={{ base: "1.8rem", md: "3.4rem" }}>
|
<Container w={{ base: "100%", md: "80%" }} p="md">
|
||||||
|
<Text
|
||||||
|
id="news-title"
|
||||||
|
ta="center"
|
||||||
|
fw={800}
|
||||||
|
c={colors["blue-button"]}
|
||||||
|
fz={{ base: "2rem", md: "3.2rem" }}
|
||||||
|
lh={{ base: "2.6rem", md: "3.6rem" }}
|
||||||
|
style={{ letterSpacing: "-0.5px" }}
|
||||||
|
>
|
||||||
{textHeading.title}
|
{textHeading.title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text id="news-content" ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>
|
|
||||||
|
<Text
|
||||||
|
id="news-content"
|
||||||
|
ta="center"
|
||||||
|
c="gray.7"
|
||||||
|
fz={{ base: "1rem", md: "1.25rem" }}
|
||||||
|
lh={{ base: "1.5rem", md: "1.9rem" }}
|
||||||
|
style={{ marginTop: 8, maxWidth: 800, marginInline: "auto" }}
|
||||||
|
>
|
||||||
{textHeading.des}
|
{textHeading.des}
|
||||||
</Text>
|
</Text>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
|
{/* LOADING STATE */}
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Stack align="center" justify="center" h={300}>
|
<Stack align="center" justify="center" h={300}>
|
||||||
<Loader size="lg" color={colors["blue-button"]} />
|
<Loader size="lg" color={colors["blue-button"]} />
|
||||||
<Text c="gray.4">Sedang memuat potensi desa...</Text>
|
<Text c="gray.4" fz="1rem" lh="1.4rem">
|
||||||
|
Sedang memuat potensi desa...
|
||||||
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
) : data.length === 0 ? (
|
) : data.length === 0 ? (
|
||||||
<Stack align="center" justify="center" h={300} gap="xs">
|
<Stack align="center" justify="center" h={300} gap="xs">
|
||||||
<IconInfoCircle size={48} color={colors["blue-button"]} />
|
<IconInfoCircle size={48} color={colors["blue-button"]} />
|
||||||
<Text fw={600} c="gray.3">
|
<Text fw={600} c="gray.3" fz="1.2rem" lh="1.4rem">
|
||||||
Belum ada potensi tersedia
|
Belum ada potensi tersedia
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" c="gray.5">
|
<Text fz="0.9rem" lh="1.3rem" c="gray.5">
|
||||||
Silakan cek kembali nanti untuk pembaruan terbaru.
|
Silakan cek kembali nanti untuk pembaruan terbaru.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
) : (
|
) : (
|
||||||
|
/* CARD LIST */
|
||||||
<SimpleGrid cols={{ base: 1, sm: 2 }}>
|
<SimpleGrid cols={{ base: 1, sm: 2 }}>
|
||||||
{_.take(data, 4).map((v, k) => (
|
{_.take(data, 4).map((v, k) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -84,7 +105,12 @@ function Potensi() {
|
|||||||
onClick={() => router.push(`/darmasaba/desa/potensi/${v.id}`)}
|
onClick={() => router.push(`/darmasaba/desa/potensi/${v.id}`)}
|
||||||
style={{ cursor: "pointer" }}
|
style={{ cursor: "pointer" }}
|
||||||
>
|
>
|
||||||
<BackgroundImage src={v.image?.link} h={320} radius={20} pos="relative">
|
<BackgroundImage
|
||||||
|
src={v.image?.link}
|
||||||
|
h={320}
|
||||||
|
radius={20}
|
||||||
|
pos="relative"
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
pos="absolute"
|
pos="absolute"
|
||||||
w="100%"
|
w="100%"
|
||||||
@@ -92,6 +118,8 @@ function Potensi() {
|
|||||||
bg={colors.trans.dark[2]}
|
bg={colors.trans.dark[2]}
|
||||||
style={{ borderRadius: 20, zIndex: 0 }}
|
style={{ borderRadius: 20, zIndex: 0 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* CARD CONTENT */}
|
||||||
<Stack
|
<Stack
|
||||||
justify="end"
|
justify="end"
|
||||||
h="100%"
|
h="100%"
|
||||||
@@ -101,11 +129,24 @@ function Potensi() {
|
|||||||
style={{ zIndex: 1 }}
|
style={{ zIndex: 1 }}
|
||||||
>
|
>
|
||||||
<Tooltip label={v.name} position="top-start">
|
<Tooltip label={v.name} position="top-start">
|
||||||
<Text fw={700} c="white" fz={{ base: "1.2rem", md: "1.4rem" }} truncate>
|
<Text
|
||||||
|
fw={700}
|
||||||
|
c="white"
|
||||||
|
fz={{ base: "1.25rem", md: "1.45rem" }}
|
||||||
|
lh={{ base: "1.6rem", md: "1.8rem" }}
|
||||||
|
truncate
|
||||||
|
>
|
||||||
{v.name}
|
{v.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Text lineClamp={2} c="gray.2" fz={{ base: "0.8rem", md: "1rem" }} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
|
|
||||||
|
<Text
|
||||||
|
lineClamp={2}
|
||||||
|
c="gray.2"
|
||||||
|
fz={{ base: "0.85rem", md: "1rem" }}
|
||||||
|
lh={{ base: "1.2rem", md: "1.4rem" }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</BackgroundImage>
|
</BackgroundImage>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -113,16 +154,18 @@ function Potensi() {
|
|||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* BUTTON */}
|
||||||
<Stack align="center">
|
<Stack align="center">
|
||||||
<Group>
|
<Group>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => router.push("/darmasaba/desa/potensi")}
|
onClick={() => router.push("/darmasaba/desa/potensi")}
|
||||||
color={colors["blue-button"]}
|
color={colors["blue-button"]}
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
gradient={{ from: "#26667F", to: "#124170", }}
|
gradient={{ from: "#26667F", to: "#124170" }}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
size="md"
|
size="md"
|
||||||
rightSection={<IconArrowRight size={18} />}
|
rightSection={<IconArrowRight size={18} />}
|
||||||
|
style={{ fontWeight: 600 }}
|
||||||
>
|
>
|
||||||
Lihat Semua Potensi
|
Lihat Semua Potensi
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -2,7 +2,19 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import prestasiState from "@/app/admin/(dashboard)/_state/landing-page/prestasi-desa";
|
import prestasiState from "@/app/admin/(dashboard)/_state/landing-page/prestasi-desa";
|
||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
import { BackgroundImage, Box, Button, Center, Container, Group, Loader, SimpleGrid, Stack, Text } from "@mantine/core";
|
import {
|
||||||
|
BackgroundImage,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Container,
|
||||||
|
Group,
|
||||||
|
Loader,
|
||||||
|
SimpleGrid,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
} from "@mantine/core";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -32,12 +44,31 @@ function Prestasi() {
|
|||||||
<Stack p="sm" bg="linear-gradient(180deg, #ffffff 0%, #f8fbff 100%)">
|
<Stack p="sm" bg="linear-gradient(180deg, #ffffff 0%, #f8fbff 100%)">
|
||||||
<Container w={{ base: "100%", md: "80%" }} p="xl">
|
<Container w={{ base: "100%", md: "80%" }} p="xl">
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<Text c={colors["blue-button"]} ta="center" fz={{ base: "2rem", md: "3.4rem" }} fw={700}>
|
|
||||||
|
{/* TITLE UTAMA */}
|
||||||
|
<Title
|
||||||
|
order={1}
|
||||||
|
c={colors["blue-button"]}
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: "2rem", sm: "2.6rem", md: "3.2rem" }}
|
||||||
|
lh={{ base: "2.4rem", md: "3.5rem" }}
|
||||||
|
>
|
||||||
Prestasi Desa
|
Prestasi Desa
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
{/* SUBTEXT */}
|
||||||
|
<Text
|
||||||
|
fz={{ base: "1rem", md: "1.2rem" }}
|
||||||
|
lh={{ base: "1.5rem", md: "1.8rem" }}
|
||||||
|
ta="center"
|
||||||
|
c="black"
|
||||||
|
maw={700}
|
||||||
|
>
|
||||||
|
Kami bangga dengan pencapaian desa hingga saat ini. Semoga prestasi ini
|
||||||
|
menjadi inspirasi untuk terus berkarya dan berinovasi demi kemajuan
|
||||||
|
bersama.
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz={{ base: "1rem", md: "1.3rem" }} ta="center" c="dimmed" maw={700}>
|
|
||||||
Kami bangga dengan pencapaian desa hingga saat ini. Semoga prestasi ini menjadi inspirasi untuk terus berkarya dan berinovasi demi kemajuan bersama.
|
|
||||||
</Text>
|
|
||||||
<Button
|
<Button
|
||||||
radius="xl"
|
radius="xl"
|
||||||
size="lg"
|
size="lg"
|
||||||
@@ -59,13 +90,13 @@ function Prestasi() {
|
|||||||
) : data.length === 0 ? (
|
) : data.length === 0 ? (
|
||||||
<Center mih={200}>
|
<Center mih={200}>
|
||||||
<Stack align="center" gap="xs">
|
<Stack align="center" gap="xs">
|
||||||
<Text fz="1.2rem" fw={500} c="dimmed">
|
<Text fz="1.2rem" fw={500} c="dimmed" ta="center" lh="1.4rem">
|
||||||
Belum ada prestasi yang ditampilkan
|
Belum ada prestasi yang ditampilkan
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg" mb={"xl"}>
|
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg" mb="xl">
|
||||||
{data.map((v, k) => (
|
{data.map((v, k) => (
|
||||||
<BackgroundImage
|
<BackgroundImage
|
||||||
key={k}
|
key={k}
|
||||||
@@ -79,26 +110,32 @@ function Prestasi() {
|
|||||||
bg="linear-gradient(180deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.7) 100%)"
|
bg="linear-gradient(180deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.7) 100%)"
|
||||||
style={{ borderRadius: 27 }}
|
style={{ borderRadius: 27 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack justify="space-between" h="100%" pos="relative" p="lg">
|
<Stack justify="space-between" h="100%" pos="relative" p="lg">
|
||||||
<Box>
|
|
||||||
|
{/* KATEGORI */}
|
||||||
<Text
|
<Text
|
||||||
c="white"
|
c="white"
|
||||||
fz={{ base: "1rem", md: "1.25rem" }}
|
fz={{ base: "1rem", md: "1.15rem" }}
|
||||||
|
lh={{ base: "1.4rem", md: "1.6rem" }}
|
||||||
ta="center"
|
ta="center"
|
||||||
fw={500}
|
fw={500}
|
||||||
>
|
>
|
||||||
{v.kategori.name}
|
{v.kategori.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
|
||||||
|
{/* DESKRIPSI */}
|
||||||
<Text
|
<Text
|
||||||
fw={700}
|
fw={700}
|
||||||
c="white"
|
c="white"
|
||||||
fz={{ base: "1.5rem", md: "2rem", lg: "2.5rem" }}
|
fz={{ base: "1.4rem", md: "1.8rem", lg: "2.2rem" }}
|
||||||
|
lh={{ base: "1.8rem", md: "2.2rem", lg: "2.6rem" }}
|
||||||
ta="center"
|
ta="center"
|
||||||
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
||||||
lineClamp={5}
|
lineClamp={5}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Group justify="center">
|
<Group justify="center">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => router.push(`/darmasaba/prestasi-desa/${v.id}`)}
|
onClick={() => router.push(`/darmasaba/prestasi-desa/${v.id}`)}
|
||||||
|
|||||||
@@ -20,12 +20,11 @@ export default function SDGS() {
|
|||||||
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
|
||||||
const result = await response.json()
|
const result = await response.json()
|
||||||
let data = []
|
let data = []
|
||||||
|
|
||||||
if (Array.isArray(result.data)) data = result.data
|
if (Array.isArray(result.data)) data = result.data
|
||||||
else if (Array.isArray(result)) data = result
|
else if (Array.isArray(result)) data = result
|
||||||
else {
|
else return setSdgsDesa([])
|
||||||
setSdgsDesa([])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const top4Sdgs = [...data].sort((a, b) => parseInt(b.jumlah) - parseInt(a.jumlah)).slice(0, 4)
|
const top4Sdgs = [...data].sort((a, b) => parseInt(b.jumlah) - parseInt(a.jumlah)).slice(0, 4)
|
||||||
setSdgsDesa(top4Sdgs)
|
setSdgsDesa(top4Sdgs)
|
||||||
} catch {
|
} catch {
|
||||||
@@ -36,24 +35,38 @@ export default function SDGS() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p="sm" my={"xs"}>
|
<Stack p="sm" my="xs">
|
||||||
<Container w={{ base: "100%", md: "80%" }} p="xl">
|
<Container w={{ base: "100%", md: "80%" }} p="xl">
|
||||||
|
|
||||||
|
{/* ========== TITLE SECTION ========== */}
|
||||||
<Center>
|
<Center>
|
||||||
<Title
|
<Title
|
||||||
order={1}
|
order={1}
|
||||||
fz={{ base: "2.4rem", md: "3.6rem" }}
|
fz={{ base: "2.2rem", md: "3.4rem" }}
|
||||||
|
lh={{ base: 1.1, md: 1.1 }}
|
||||||
fw={900}
|
fw={900}
|
||||||
c={colors["blue-button"]}
|
c={colors["blue-button"]}
|
||||||
|
ta="center"
|
||||||
>
|
>
|
||||||
SDGs Desa
|
SDGs Desa
|
||||||
</Title>
|
</Title>
|
||||||
</Center>
|
</Center>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>
|
|
||||||
SDGs Desa merupakan langkah nyata untuk mewujudkan desa yang maju, inklusif, dan berkelanjutan melalui 17 tujuan pembangunan dari pengentasan kemiskinan, pendidikan, kesehatan, kesetaraan gender, hingga pelestarian lingkungan.
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: "1rem", md: "1.2rem" }}
|
||||||
|
lh={{ base: 1.5, md: 1.6 }}
|
||||||
|
c="black"
|
||||||
|
mt="xs"
|
||||||
|
mb="md"
|
||||||
|
>
|
||||||
|
SDGs Desa adalah upaya desa untuk menciptakan pembangunan yang maju, inklusif, dan berkelanjutan melalui 17 tujuan mulai dari pengentasan kemiskinan, pendidikan, kesehatan, hingga pelestarian lingkungan.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Box py="lg">
|
<Box py="lg">
|
||||||
{sdgsDesa && sdgsDesa.length > 0 ? (
|
{sdgsDesa && sdgsDesa.length > 0 ? (
|
||||||
|
|
||||||
|
/* ========== LIST GRID ========== */
|
||||||
<SimpleGrid cols={{ base: 1, sm: 4 }} spacing="xl" verticalSpacing="xl" pb={30}>
|
<SimpleGrid cols={{ base: 1, sm: 4 }} spacing="xl" verticalSpacing="xl" pb={30}>
|
||||||
{sdgsDesa.map((item) => (
|
{sdgsDesa.map((item) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -70,7 +83,7 @@ export default function SDGS() {
|
|||||||
background: "linear-gradient(180deg, #FFFFFF, #F6F8FA)",
|
background: "linear-gradient(180deg, #FFFFFF, #F6F8FA)",
|
||||||
border: "1px solid rgba(0,0,0,0.05)",
|
border: "1px solid rgba(0,0,0,0.05)",
|
||||||
transition: "all 0.3s ease",
|
transition: "all 0.3s ease",
|
||||||
height: "100%", // biar tinggi antar card konsisten
|
height: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
@@ -101,23 +114,26 @@ export default function SDGS() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Center>
|
</Center>
|
||||||
|
|
||||||
{/* Stack isi teks & angka */}
|
|
||||||
<Stack justify="space-between" align="center" gap="xs" h="100%">
|
<Stack justify="space-between" align="center" gap="xs" h="100%">
|
||||||
|
|
||||||
|
{/* JUDUL ITEM */}
|
||||||
<Text
|
<Text
|
||||||
ta="center"
|
ta="center"
|
||||||
fz={{ base: "lg", md: "xl" }}
|
fz={{ base: "lg", md: "xl" }}
|
||||||
|
lh={{ base: 1.3, md: 1.3 }}
|
||||||
fw={700}
|
fw={700}
|
||||||
mb="xs"
|
style={{ minHeight: mobile ? 60 : 70 }}
|
||||||
style={{ minHeight: mobile ? 60 : 70 }} // biar judulnya punya tinggi tetap
|
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{/* ANGKA */}
|
||||||
<Title
|
<Title
|
||||||
order={2}
|
order={2}
|
||||||
ta="center"
|
ta="center"
|
||||||
style={{
|
style={{
|
||||||
fontSize: mobile ? "2.4rem" : "3.2rem",
|
fontSize: mobile ? "2.2rem" : "3rem",
|
||||||
|
lineHeight: 1.1,
|
||||||
fontWeight: 900,
|
fontWeight: 900,
|
||||||
letterSpacing: "-0.5px",
|
letterSpacing: "-0.5px",
|
||||||
color: "#124170",
|
color: "#124170",
|
||||||
@@ -132,14 +148,15 @@ export default function SDGS() {
|
|||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
|
||||||
) : (
|
) : (
|
||||||
|
|
||||||
|
/* ========== EMPTY STATE ========== */
|
||||||
<Center mih={200} style={{ flexDirection: "column" }}>
|
<Center mih={200} style={{ flexDirection: "column" }}>
|
||||||
<IconMoodSad size={48} stroke={1.5} style={{ marginBottom: "1rem" }} />
|
<IconMoodSad size={48} stroke={1.5} style={{ marginBottom: "1rem" }} />
|
||||||
<Text fz="lg" c="dimmed">
|
<Text fz="lg" lh={1.4} c="dimmed">Data SDGs Desa belum tersedia</Text>
|
||||||
Data SDGs Desa belum tersedia
|
|
||||||
</Text>
|
|
||||||
</Center>
|
</Center>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* BUTTON */}
|
||||||
<Center>
|
<Center>
|
||||||
<Button
|
<Button
|
||||||
component={Link}
|
component={Link}
|
||||||
@@ -152,18 +169,19 @@ export default function SDGS() {
|
|||||||
style={{
|
style={{
|
||||||
boxShadow: "0 6px 14px rgba(18,65,112,0.25)",
|
boxShadow: "0 6px 14px rgba(18,65,112,0.25)",
|
||||||
transition: "all 0.3s ease",
|
transition: "all 0.3s ease",
|
||||||
transform: "translateY(0)",
|
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
e.currentTarget.style.transform = "translateY(-4px)";
|
e.currentTarget.style.transform = "translateY(-4px)"
|
||||||
e.currentTarget.style.boxShadow = "0 10px 20px rgba(18,65,112,0.35)";
|
e.currentTarget.style.boxShadow = "0 10px 20px rgba(18,65,112,0.35)"
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
e.currentTarget.style.transform = "translateY(0)";
|
e.currentTarget.style.transform = "translateY(0)"
|
||||||
e.currentTarget.style.boxShadow = "0 6px 14px rgba(18,65,112,0.25)";
|
e.currentTarget.style.boxShadow = "0 6px 14px rgba(18,65,112,0.25)"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text c="white" fz={{ base: "md", md: "lg" }} fw="bold">Jelajahi Semua Tujuan SDGs Desa</Text>
|
<Text c="white" fz={{ base: "md", md: "lg" }} lh={1.3} fw={600}>
|
||||||
|
Jelajahi Semua Tujuan SDGs Desa
|
||||||
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -158,8 +158,6 @@ export default function Page() {
|
|||||||
<SDGS />
|
<SDGS />
|
||||||
<Apbdes />
|
<Apbdes />
|
||||||
<Prestasi />
|
<Prestasi />
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<ScrollToTopButton />
|
<ScrollToTopButton />
|
||||||
<NewsReaderLanding />
|
<NewsReaderLanding />
|
||||||
|
|
||||||
@@ -170,6 +168,8 @@ export default function Page() {
|
|||||||
onSeen={handleSeen}
|
onSeen={handleSeen}
|
||||||
autoShowDelay={2000}
|
autoShowDelay={2000}
|
||||||
/>
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@ const navbarListMenu = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "1.1",
|
id: "1.1",
|
||||||
name: "Profile PPID",
|
name: "Profil PPID",
|
||||||
href: "/darmasaba/ppid/profile-ppid"
|
href: "/darmasaba/ppid/profil-ppid"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "1.2",
|
id: "1.2",
|
||||||
@@ -53,8 +53,8 @@ const navbarListMenu = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "2.1",
|
id: "2.1",
|
||||||
name: "Profile",
|
name: "Profil",
|
||||||
href: "/darmasaba/desa/profile"
|
href: "/darmasaba/desa/profil"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "2.2",
|
id: "2.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user