From 0b574406e23af0d4a86b682cbe44408fe91bb5b9 Mon Sep 17 00:00:00 2001 From: nico Date: Wed, 15 Oct 2025 17:29:57 +0800 Subject: [PATCH] Fix QC Kak Inno : tanggal 14 Oktober Fitur Search bisa digunakan di 6 Menu, sisa 3 Menu Lagi --- .../landing-page/profile/_lib/layoutTabs.tsx | 2 +- .../api/[[...slugs]]/_lib/search/findMany.ts | 663 +++++++++++++++++- .../[[...slugs]]/_lib/search/searchState.ts | 144 +++- .../darmasaba/(pages)/desa/profile/page.tsx | 49 +- .../kesehatan/data-kesehatan-warga/page.tsx | 37 +- .../ppid/permohonan-informasi-publik/page.tsx | 22 +- .../(pages)/ppid/profile-ppid/page.tsx | 189 ++--- .../(pages)/ppid/struktur-ppid/[id]/page.tsx | 157 +++++ .../(pages)/ppid/struktur-ppid/page.tsx | 158 +---- .../(pages)/ppid/visi-misi-ppid/page.tsx | 4 +- src/app/darmasaba/_com/NavBarSearch.tsx | 69 +- src/app/darmasaba/_com/globalSearch.tsx | 86 ++- .../_com/main-page/penghargaan/index.tsx | 2 +- src/app/darmasaba/_com/scrollToTopButton.tsx | 36 + src/app/darmasaba/_com/searchUrl.tsx | 60 ++ src/app/darmasaba/page.tsx | 29 +- 16 files changed, 1300 insertions(+), 407 deletions(-) create mode 100644 src/app/darmasaba/(pages)/ppid/struktur-ppid/[id]/page.tsx create mode 100644 src/app/darmasaba/_com/scrollToTopButton.tsx create mode 100644 src/app/darmasaba/_com/searchUrl.tsx diff --git a/src/app/admin/(dashboard)/landing-page/profile/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/landing-page/profile/_lib/layoutTabs.tsx index d2dd5ab9..24595d4b 100644 --- a/src/app/admin/(dashboard)/landing-page/profile/_lib/layoutTabs.tsx +++ b/src/app/admin/(dashboard)/landing-page/profile/_lib/layoutTabs.tsx @@ -66,7 +66,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) { return ( - Profil Desa + Profile Desa ({ type: "pengumuman", ...b })), ...galleryFoto.map((b) => ({ type: "galleryFoto", ...b })), ...galleryVideo.map((b) => ({ type: "galleryVideo", ...b })), - ...pelayananSuratKeterangan.map((b) => ({ type: "pelayananSuratKeterangan", ...b })), - ...pelayananPerizinanBerusaha.map((b) => ({ type: "pelayananPerizinanBerusaha", ...b })), - ...pelayananTelunjukSaktiDesa.map((b) => ({ type: "pelayananTelunjukSaktiDesa", ...b })), - ...pelayananPendudukNonPermanent.map((b) => ({ type: "pelayananPendudukNonPermanent", ...b })), + ...pelayananSuratKeterangan.map((b) => ({ + type: "pelayananSuratKeterangan", + ...b, + })), + ...pelayananPerizinanBerusaha.map((b) => ({ + type: "pelayananPerizinanBerusaha", + ...b, + })), + ...pelayananTelunjukSaktiDesa.map((b) => ({ + type: "pelayananTelunjukSaktiDesa", + ...b, + })), + ...pelayananPendudukNonPermanen.map((b) => ({ + type: "pelayananPendudukNonPermanen", + ...b, + })), ...penghargaan.map((b) => ({ type: "penghargaan", ...b })), + ...posyandu.map((b) => ({ type: "posyandu", ...b })), + ...fasilitasKesehatan.map((b) => ({ type: "fasilitasKesehatan", ...b })), + ...jadwalKegiatan.map((b) => ({ type: "jadwalKegiatan", ...b })), + ...artikelKesehatan.map((b) => ({ type: "artikelKesehatan", ...b })), + ...puskesmas.map((b) => ({ type: "puskesmas", ...b })), + ...programKesehatan.map((b) => ({ type: "programKesehatan", ...b })), + ...penangananDarurat.map((b) => ({ type: "penangananDarurat", ...b })), + ...kontakDarurat.map((b) => ({ type: "kontakDarurat", ...b })), + ...infoWabahPenyakit.map((b) => ({ type: "infoWabahPenyakit", ...b })), + ...keamananLingkungan.map((b) => ({ type: "keamananLingkungan", ...b })), + ...polsekTerdekat.map((b) => ({ type: "polsekTerdekat", ...b })), + ...kontakDaruratKeamanan.map((b) => ({ type: "kontakDaruratKeamanan", ...b })), + ...pencegahanKriminalitas.map((b) => ({ type: "pencegahanKriminalitas", ...b })), + ...laporanPublik.map((b) => ({ type: "laporanPublik", ...b })), + ...tipsKeamanan.map((b) => ({ type: "tipsKeamanan", ...b })), + ...pasarDesa.map((b) => ({ type: "pasarDesa", ...b })), + ...lowonganKerjaLokal.map((b) => ({ type: "lowonganKerjaLokal", ...b })), + ...strukturOrganisasi.map((b) => ({ type: "strukturOrganisasi", ...b })), + ...jumlahPendudukUsiaKerjaYangMenganggurUsia.map((b) => ({ type: "jumlahPendudukUsiaKerjaYangMenganggurUsia", ...b })), + ...jumlahPendudukUsiaKerjaYangMenganggurPendidikan.map((b) => ({ type: "jumlahPendudukUsiaKerjaYangMenganggurPendidikan", ...b })), + ...jumlahPendudukMiskin.map((b) => ({ type: "jumlahPendudukMiskin", ...b })), + ...programKemiskinan.map((b) => ({ type: "programKemiskinan", ...b })), + ...sektorUnggulanDesa.map((b) => ({ type: "sektorUnggulanDesa", ...b })), + ...demografiPekerjaan.map((b) => ({ type: "demografiPekerjaan", ...b })), + ], nextPage: null, // bisa dibuat lebih kompleks kalau perlu }; diff --git a/src/app/api/[[...slugs]]/_lib/search/searchState.ts b/src/app/api/[[...slugs]]/_lib/search/searchState.ts index e8b6e9d1..d4951d64 100644 --- a/src/app/api/[[...slugs]]/_lib/search/searchState.ts +++ b/src/app/api/[[...slugs]]/_lib/search/searchState.ts @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ 'use client'; -import { proxy, subscribe } from 'valtio'; +import { proxy } from 'valtio'; import { debounce } from 'lodash'; import ApiFetch from '@/lib/api-fetch'; interface SearchResult { - type?: string; // optional biar gak error + type?: string; id: string | number; title?: string; [key: string]: any; @@ -21,36 +21,30 @@ const searchState = proxy({ loading: false, async fetch() { - if (!searchState.query) return; + if (!searchState.query) { + searchState.results = []; + return; + } + searchState.loading = true; - try { - const res = await ApiFetch.api.search.findMany.get({ - query: { - query: searchState.query, - page: searchState.page, - limit: searchState.limit, - type: searchState.type, - }, - }); + const res = await ApiFetch.api.search.findMany.get({ + query: { + query: searchState.query, + page: searchState.page, + limit: searchState.limit, + type: searchState.type, + }, + }); - const data = (res.data?.data || []).map((item: any) => ({ - type: item.type ?? 'unknown', // pastikan selalu ada type - ...item, - })); - - if (searchState.page === 1) { - searchState.results = data; - } else { - searchState.results.push(...data); - } - - searchState.nextPage = res.data?.nextPage || null; - } catch (e) { - console.error('Search fetch error:', e); - } finally { - searchState.loading = false; + if (searchState.page === 1) { + searchState.results = res.data?.data || []; + } else { + searchState.results.push(...(res.data?.data || [])); } + + searchState.nextPage = res.data?.nextPage || null; + searchState.loading = false; }, async next() { @@ -60,15 +54,95 @@ const searchState = proxy({ }, }); -// ๐Ÿ” Auto debounce search trigger -const debouncedFetch = debounce(() => { - if (!searchState.query) return; +// ๐Ÿ•’ debounce-nya tetap kita export biar bisa dipanggil manual +export const debouncedFetch = debounce(() => { searchState.page = 1; searchState.fetch(); }, 500); -subscribe(searchState, () => { - debouncedFetch(); -}); - export default searchState; + + +// 'use client'; +// import { proxy, subscribe } from 'valtio'; +// import { debounce } from 'lodash'; +// import ApiFetch from '@/lib/api-fetch'; + +// interface SearchResult { +// type?: string; +// id: string | number; +// title?: string; +// [key: string]: any; +// } + +// const searchState = proxy({ +// query: '', +// page: 1, +// limit: 10, +// type: '', // kosong = global search +// results: [] as SearchResult[], +// nextPage: null as number | null, +// loading: false, + +// // --- fetch utama --- +// async fetch() { +// if (!searchState.query.trim()) { +// // ๐Ÿงน kalau query kosong, kosongin data dan stop +// searchState.results = []; +// searchState.nextPage = null; +// searchState.loading = false; +// return; +// } + +// searchState.loading = true; + +// try { +// const res = await ApiFetch.api.search.findMany.get({ +// query: { +// query: searchState.query, +// page: searchState.page, +// limit: searchState.limit, +// type: searchState.type, +// }, +// }); + +// const newData = res.data?.data || []; + +// // Kalau ini page pertama, replace data +// if (searchState.page === 1) { +// searchState.results = newData; +// } else { +// // Kalau page berikutnya, append data +// searchState.results = [...searchState.results, ...newData]; +// } + +// searchState.nextPage = res.data?.nextPage || null; +// } catch (err) { +// console.error('Search fetch error:', err); +// } finally { +// searchState.loading = false; +// } +// }, + +// // --- load next page (infinite scroll) --- +// async next() { +// if (!searchState.nextPage || searchState.loading) return; +// searchState.page = searchState.nextPage; +// await searchState.fetch(); +// }, +// }); + +// // --- debounce agar gak fetch tiap ketik --- +// const debouncedFetch = debounce(() => { +// // reset pagination setiap query berubah +// searchState.page = 1; +// searchState.fetch(); +// }, 500); + +// // --- auto trigger setiap query berubah --- +// subscribe(searchState, () => { +// // kalau query berubah, jalankan debounce fetch +// debouncedFetch(); +// }); + +// export default searchState; diff --git a/src/app/darmasaba/(pages)/desa/profile/page.tsx b/src/app/darmasaba/(pages)/desa/profile/page.tsx index de4f921f..e2f08dd5 100644 --- a/src/app/darmasaba/(pages)/desa/profile/page.tsx +++ b/src/app/darmasaba/(pages)/desa/profile/page.tsx @@ -10,31 +10,36 @@ import ProfilPerbekel from './ui/profilPerbekel'; // import LembagaDesa from './ui/lembagaDesa'; import MotoDesa from './ui/motoDesa'; import SemuaPerbekel from './ui/semuaPerbekel'; +import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton'; function Page() { return ( - - - - - - - - Profile Desa - - - - - - - - - - - - - - + + + + + + + + + Profile Desa + + + + + + + + + + + + + + + {/* Tombol Scroll ke Atas */} + + ); } diff --git a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/page.tsx b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/page.tsx index 31c1ee63..3a25f243 100644 --- a/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/page.tsx +++ b/src/app/darmasaba/(pages)/kesehatan/data-kesehatan-warga/page.tsx @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import colors from '@/con/colors'; import { BarChart as MantineBarChart } from '@mantine/charts'; -import { Box, Center, ColorSwatch, Flex, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core'; +import { Box, Center, ColorSwatch, Flex, Paper, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core'; import { useEffect, useState } from 'react'; import BackButton from '../../desa/layanan/_com/BackButto'; @@ -107,20 +107,7 @@ function Page() { - - - - Angka Kematian - - - - - - Angka Kelahiran - - - - + Data Kematian dan Kelahiran {chartData.length === 0 ? ( Belum ada data yang tersedia untuk ditampilkan @@ -150,6 +137,20 @@ function Page() { )} + + + + Angka Kematian + + + + + + Angka Kelahiran + + + + @@ -163,11 +164,11 @@ function Page() { }} > {/* Fasilitas Kesehatan */} - + {/* Jadwal Kegiatan */} - + {/* Artikel Kesehatan */} - + diff --git a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx index 66068037..5a47ba67 100644 --- a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx +++ b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx @@ -12,10 +12,9 @@ import { SimpleGrid, Stack, Text, - TextInput, - Tooltip, + TextInput } from '@mantine/core'; -import { IconDownload, IconSend2 } from '@tabler/icons-react'; +import { IconSend2 } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useProxy } from 'valtio/utils'; import BackButton from '../../desa/layanan/_com/BackButto'; @@ -150,23 +149,6 @@ function Page() { ))} - -
- - - -
- - - - - - - Profil PPID Desa Darmasaba - - - {dataArray.map((item) => ( - - - - - Logo Desa - - Pejabat Pengelola Informasi Publik - - - - - - - - - - -
- Foto Pimpinan -
- - - {item.name} - - -
-
-
- - - - - - - Biografi - - - - - - - Riwayat Karir - - - - - -
-
- - - - - Pengalaman Organisasi - - - - - - - - - - - - Program Unggulan - - - - - - - -
+ + + + - ))} - + + + Profil PPID Desa Darmasaba + + + {dataArray.map((item) => ( + + + + + Logo Desa + + Pejabat Pengelola Informasi Publik + + + + + + + + + + +
+ Foto Pimpinan +
+ + + {item.name} + + +
+
+
+ + + + + + + Biografi + + + + + + + Riwayat Karir + + + + + +
+
+ + + + + Pengalaman Organisasi + + + + + + + + + + + + Program Unggulan + + + + + + + +
+
+ ))} + + {/* Tombol Scroll ke Atas */} + +
) } diff --git a/src/app/darmasaba/(pages)/ppid/struktur-ppid/[id]/page.tsx b/src/app/darmasaba/(pages)/ppid/struktur-ppid/[id]/page.tsx new file mode 100644 index 00000000..36a02bc3 --- /dev/null +++ b/src/app/darmasaba/(pages)/ppid/struktur-ppid/[id]/page.tsx @@ -0,0 +1,157 @@ +'use client'; +import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID'; +import colors from '@/con/colors'; +import { + Box, + Divider, + Group, + Image, + Paper, + Skeleton, + Stack, + Text, + Title, +} from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useProxy } from 'valtio/utils'; + +function DetailPegawaiUser() { + const statePegawai = useProxy(stateStrukturPPID.pegawai); + const params = useParams(); + const router = useRouter(); + + useShallowEffect(() => { + stateStrukturPPID.posisiOrganisasi.findMany.load(); + statePegawai.findUnique.load(params?.id as string); + }, []); + + + if (!statePegawai.findUnique.data) { + return ( + + + + ); + } + + const data = statePegawai.findUnique.data; + + return ( + + {/* Back button */} + + router.back()} + style={{ + cursor: 'pointer', + display: 'flex', + alignItems: 'center', + gap: 8, + }} + > + + + Kembali + + + + + + + {/* Foto Profil */} + {data.namaLengkap + + {/* Nama & Jabatan */} + + + {data.namaLengkap || '-'} {data.gelarAkademik || ''} + + + {data.posisi?.nama || 'Posisi tidak tersedia'} + + + + + + + {/* Informasi Detail */} + + + + + + + + + + ); +} + +/* Komponen kecil untuk menampilkan baris informasi */ +function InfoRow({ + label, + value, + valueColor, + multiline = false, +}: { + label: string; + value?: string | null; + valueColor?: string; + multiline?: boolean; +}) { + return ( + + + {label} + + + {value || '-'} + + + ); +} + +export default DetailPegawaiUser; diff --git a/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx b/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx index b78bc11e..4b9c52b2 100644 --- a/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx +++ b/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx @@ -1,129 +1,8 @@ /* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable @typescript-eslint/no-explicit-any */ -// /* eslint-disable react-hooks/exhaustive-deps */ -// /* eslint-disable @typescript-eslint/no-explicit-any */ -// 'use client' -// import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID'; -// import colors from '@/con/colors'; -// import { Box, Image, Paper, Skeleton, Stack, Text, Title } from '@mantine/core'; -// import { OrganizationChart } from 'primereact/organizationchart'; -// import { useEffect } from 'react'; -// import { useProxy } from 'valtio/utils'; -// import BackButton from '../../desa/layanan/_com/BackButto'; - -// function Page() { -// return ( -// -// -// -// -// Struktur PPID -// - -// -// ); -// } - -// function StrukturOrganisasiPPID() { -// const stateOrganisasi = useProxy(stateStrukturPPID.pegawai) - -// useEffect(() => { -// stateOrganisasi.findMany.load() -// }, []) - -// if (!stateOrganisasi.findMany.data || stateOrganisasi.findMany.data.length === 0) { -// return ( -// -// -// -// ); -// } - -// // Step 1: Group pegawai berdasarkan posisiId -// const posisiMap = new Map(); - -// for (const pegawai of stateOrganisasi.findMany.data) { -// const posisiId = pegawai.posisi.id; -// if (!posisiMap.has(posisiId)) { -// posisiMap.set(posisiId, { -// ...pegawai.posisi, -// pegawaiList: [], -// children: [] -// }); -// } -// posisiMap.get(posisiId)!.pegawaiList.push(pegawai); -// } - - -// // Step 2: Buat struktur pohon berdasarkan parentId -// const root: any[] = []; - -// posisiMap.forEach((posisi) => { -// if (posisi.parentId) { -// const parent = posisiMap.get(posisi.parentId); -// if (parent) { -// parent.children.push(posisi); -// } -// } else { -// root.push(posisi); -// } -// }); - -// // Step 3: Ubah struktur ke format OrganizationChart -// function toOrgChartFormat(node: any): any { -// return { -// expanded: true, -// type: 'person', -// styleClass: 'p-person', -// data: { -// name: node.pegawaiList?.[0]?.namaLengkap || 'Tidak ada pegawai', -// status: node.nama, -// image: node.pegawaiList?.[0]?.image?.link || '/img/default.png' -// }, -// children: node.children.map(toOrgChartFormat) -// }; -// } - - -// const chartData = root.map(toOrgChartFormat); - -// return ( -// -// -// -// -// -// ); -// } - - -// function nodeTemplate(node: any) { -// const imageSrc = node?.data?.image || '/img/default.png'; -// const name = node?.data?.name || 'Tanpa Nama'; -// const status = node?.data?.status || 'Tidak ada deskripsi'; - -// return ( -// -// -// {name} -// {name} -// {status} -// -// -// ); -// } - -// export default Page; - 'use client' import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID' +import colors from '@/con/colors' import { Box, Button, @@ -145,7 +24,8 @@ import { OrganizationChart } from 'primereact/organizationchart' import { useEffect } from 'react' import { useProxy } from 'valtio/utils' import BackButton from '../../desa/layanan/_com/BackButto' -import colors from '@/con/colors' +import { useTransitionRouter } from 'next-view-transitions' +import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton' export default function Page() { return ( @@ -180,12 +60,16 @@ export default function Page() {
+ + {/* Tombol Scroll ke Atas */} + ) } function StrukturOrganisasiPPID() { const stateOrganisasi: any = useProxy(stateStrukturPPID.pegawai) + const router = useTransitionRouter() useEffect(() => { void stateOrganisasi.findMany.load() @@ -292,19 +176,21 @@ function StrukturOrganisasiPPID() { }) function toOrgChartFormat(node: any): any { + const pegawai = node.pegawaiList?.[0]; return { expanded: true, type: 'person', styleClass: 'p-person', data: { - name: node.pegawaiList?.[0]?.namaLengkap || 'Belum ditugaskan', + id: pegawai?.id || null, // tambahin ini bro + name: pegawai?.namaLengkap || 'Belum ditugaskan', title: node.nama || 'Tanpa jabatan', - image: node.pegawaiList?.[0]?.image?.link || '/img/default.png', + image: pegawai?.image?.link || '/img/default.png', description: node.deskripsi || '', positionId: node.id || null, }, children: node.children?.map(toOrgChartFormat) || [], - } + }; } const chartData = root.map(toOrgChartFormat) @@ -322,21 +208,22 @@ function StrukturOrganisasiPPID() { > nodeTemplate(node, router)} />
) } -function nodeTemplate(node: any) { +function nodeTemplate(node: any, router: ReturnType) { const imageSrc = node?.data?.image || '/img/default.png' const name = node?.data?.name || 'Tanpa Nama' const title = node?.data?.title || 'Tanpa Jabatan' const description = node?.data?.description || '' + return ( - + {(styles) => ( {description || 'Belum ada deskripsi.'} - + @@ -394,6 +279,3 @@ function nodeTemplate(node: any) { ) } - - - diff --git a/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx b/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx index 45a8062e..08bdc71b 100644 --- a/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx +++ b/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx @@ -73,7 +73,7 @@ function Page() { @@ -88,7 +88,7 @@ function Page() { diff --git a/src/app/darmasaba/_com/NavBarSearch.tsx b/src/app/darmasaba/_com/NavBarSearch.tsx index b7fca50a..9e690472 100644 --- a/src/app/darmasaba/_com/NavBarSearch.tsx +++ b/src/app/darmasaba/_com/NavBarSearch.tsx @@ -1,20 +1,63 @@ +import { useRef, useState, useEffect } from 'react'; import stateNav from "@/state/state-nav"; -import { Container, Stack, Tooltip } from "@mantine/core"; +import { Container, Stack, ActionIcon, Box } from "@mantine/core"; +import { IconX } from '@tabler/icons-react'; import GlobalSearch from "./globalSearch"; export function NavbarSearch() { + const [isOpen, setIsOpen] = useState(false); + const containerRef = useRef(null); + + // Close when clicking outside + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if (containerRef.current && !containerRef.current.contains(event.target as Node)) { + setIsOpen(false); + stateNav.clear(); + } + } + + // Add event listener + document.addEventListener('mousedown', handleClickOutside); + return () => { + // Clean up + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + return ( - - - - - - - + + + + + {isOpen && ( + { + setIsOpen(false); + stateNav.clear(); + }} + style={{ + position: 'absolute', + right: 10, + top: '50%', + transform: 'translateY(-50%)', + zIndex: 1000 + }} + > + + + )} + + + + ); -} +} \ No newline at end of file diff --git a/src/app/darmasaba/_com/globalSearch.tsx b/src/app/darmasaba/_com/globalSearch.tsx index 259866f7..fda58114 100644 --- a/src/app/darmasaba/_com/globalSearch.tsx +++ b/src/app/darmasaba/_com/globalSearch.tsx @@ -1,53 +1,17 @@ 'use client'; -import { TextInput, Loader, Stack, Box, Text } from '@mantine/core'; -import { useSnapshot } from 'valtio'; -import { useRouter } from 'next/navigation'; +import searchState, { debouncedFetch } from '@/app/api/[[...slugs]]/_lib/search/searchState'; +import { Box, Center, Loader, Stack, Text, TextInput } from '@mantine/core'; +import { IconX } from '@tabler/icons-react'; import { useEffect } from 'react'; -import searchState from '@/app/api/[[...slugs]]/_lib/search/searchState'; +import { useSnapshot } from 'valtio'; +import getDetailUrl from './searchUrl'; -// Mapping type ke URL -const getDetailUrl = (item: { type?: string; id: string | number; [key: string]: unknown }) => { - const { type, id, kategori } = item; - - const typeUrlMap: Record = { - programinovasi: `/darmasaba/program-inovasi/${id}`, - desaantikorupsi: '/darmasaba/desa-anti-korupsi', - sdgsdesa: '/darmasaba/sdgs-desa', - apbdes: '/darmasaba/apbdes', - prestasidesa: '/darmasaba/prestasi-desa', - pejabatdesa: '/darmasaba/profile/pejabat-desa', - strukturppid: '/darmasaba/ppid/struktur-ppid', - visimisippid: '/darmasaba/ppid/visi-misi', - dasarhukumppid: '/darmasaba/ppid/dasar-hukum', - profileppid: '/darmasaba/ppid/profile', - daftarinformasipublik: '/darmasaba/ppid/daftar-informasi-publik', - perbekeldarmasaba: '/darmasaba/desa/profile', - berita: `/darmasaba/desa/berita/${kategori}/${id}`, - pengumuman: `/darmasaba/desa/pengumuman/${kategori}/${id}`, - sejarahdesa: '/darmasaba/desa/profile', - visimisidesa: '/darmasaba/desa/profile', - lambangdesa: '/darmasaba/desa/profile', - maskotdesa: '/darmasaba/desa/profile', - profilperbekel: '/darmasaba/desa/profile', - potensi: '/darmasaba/desa/potensi-desa', - galleryFoto: '/darmasaba/desa/gallery/foto', - galleryVideo: '/darmasaba/desa/gallery/video', - pelayananSuratKeterangan: '/darmasaba/desa/layanan', - pelayananPerizinanBerusaha: '/darmasaba/desa/layanan', - pelayananTelunjukSaktiDesa: '/darmasaba/desa/layanan', - pelayananPendudukNonPermanent: '/darmasaba/desa/layanan', - penghargaan: '/darmasaba/desa/penghargaan', - }; - - return type ? typeUrlMap[type] || '/darmasaba' : '/darmasaba'; -}; export default function GlobalSearch() { const snap = useSnapshot(searchState); - const router = useRouter(); - // Infinite scroll listener + // Infinite scroll useEffect(() => { const handleScroll = () => { const bottom = @@ -59,13 +23,32 @@ export default function GlobalSearch() { }, [snap.loading]); return ( - + + {/* ๐Ÿ” Search input */} (searchState.query = e.currentTarget.value)} + onChange={(e) => ( + searchState.query = e.currentTarget.value, + debouncedFetch() + )} + radius="xl" + rightSection={ + snap.query ? ( + { + searchState.query = ''; + searchState.results = []; + }} + /> + ) : undefined + } /> + {/* ๐Ÿ“„ Hasil pencarian */} +
{snap.results.map((item, i) => ( (e.currentTarget.style.background = '#f5f5f5')} onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')} onClick={() => { const url = getDetailUrl(item); - router.push(url); + window.location.href = url; }} > @@ -90,8 +77,15 @@ export default function GlobalSearch() { ))} +
- {snap.loading && } + {/* โณ Loader di bawah hasil */} + {snap.loading && ( +
+ +
+ )}
); } + diff --git a/src/app/darmasaba/_com/main-page/penghargaan/index.tsx b/src/app/darmasaba/_com/main-page/penghargaan/index.tsx index 9f2d1496..46ef6f5c 100644 --- a/src/app/darmasaba/_com/main-page/penghargaan/index.tsx +++ b/src/app/darmasaba/_com/main-page/penghargaan/index.tsx @@ -68,7 +68,7 @@ function Penghargaan() { variant="gradient" gradient={{ from: "cyan", to: "blue", deg: 60 }} > - Penghargaan & Prestasi Desa + Penghargaan Desa
{loading ? ( diff --git a/src/app/darmasaba/_com/scrollToTopButton.tsx b/src/app/darmasaba/_com/scrollToTopButton.tsx new file mode 100644 index 00000000..c2b66fe7 --- /dev/null +++ b/src/app/darmasaba/_com/scrollToTopButton.tsx @@ -0,0 +1,36 @@ +'use client' +import { useWindowScroll } from '@mantine/hooks'; +import { ActionIcon, Transition } from '@mantine/core'; +import { IconArrowUp } from '@tabler/icons-react'; +import colors from '@/con/colors'; + +function ScrollToTopButton() { + const [scroll, scrollTo] = useWindowScroll(); + + return ( + 300} + transition="slide-up" + duration={300} + timingFunction="ease" + > + {(styles) => ( + scrollTo({ y: 0 })} + pos="fixed" + bottom={24} + right={24} + aria-label="Kembali ke atas" + > + + + )} + + ); +} +export default ScrollToTopButton \ No newline at end of file diff --git a/src/app/darmasaba/_com/searchUrl.tsx b/src/app/darmasaba/_com/searchUrl.tsx new file mode 100644 index 00000000..d358bfa9 --- /dev/null +++ b/src/app/darmasaba/_com/searchUrl.tsx @@ -0,0 +1,60 @@ +const getDetailUrl = (item: { type?: string; id: string | number; [key: string]: unknown }) => { + const { type, id, kategori } = item; + const typeUrlMap: Record = { + programinovasi: `/darmasaba/program-inovasi/${id}`, + desaantikorupsi: '/darmasaba/desa-anti-korupsi', + sdgsdesa: '/darmasaba/sdgs-desa', + apbdes: '/darmasaba/apbdes', + prestasidesa: '/darmasaba/prestasi-desa', + pejabatdesa: '/darmasaba/profile/pejabat-desa', + strukturppid: '/darmasaba/ppid/struktur-ppid', + visimisippid: '/darmasaba/ppid/visi-misi', + dasarhukumppid: '/darmasaba/ppid/dasar-hukum', + profileppid: '/darmasaba/ppid/profile', + daftarinformasipublik: '/darmasaba/ppid/daftar-informasi-publik', + perbekeldarmasaba: '/darmasaba/desa/profile', + berita: `/darmasaba/desa/berita/${kategori}/${id}`, + pengumuman: `/darmasaba/desa/pengumuman/${kategori}/${id}`, + sejarahdesa: '/darmasaba/desa/profile', + visimisidesa: '/darmasaba/desa/profile', + lambangdesa: '/darmasaba/desa/profile', + maskotdesa: '/darmasaba/desa/profile', + profilperbekel: '/darmasaba/desa/profile', + potensi: '/darmasaba/desa/potensi-desa', + galleryFoto: '/darmasaba/desa/gallery/foto', + galleryVideo: '/darmasaba/desa/gallery/video', + pelayananSuratKeterangan: '/darmasaba/desa/layanan', + pelayananPerizinanBerusaha: '/darmasaba/desa/layanan', + pelayananTelunjukSaktiDesa: '/darmasaba/desa/layanan', + pelayananPendudukNonPermanent: '/darmasaba/desa/layanan', + penghargaan: '/darmasaba/desa/penghargaan', + posyandu: '/darmasaba/kesehatan/posyandu', + fasilitasKesehatan: '/darmasaba/kesehatan/data-kesehatan-warga', + jadwalKegiatan: '/darmasaba/kesehatan/data-kesehatan-warga', + artikelKesehatan: '/darmasaba/kesehatan/data-kesehatan-warga', + puskesmas: '/darmasaba/kesehatan/puskesmas', + programKesehatan: '/darmasaba/kesehatan/program-kesehatan', + penangananDarurat: '/darmasaba/kesehatan/penanganan-darurat', + kontakDarurat: '/darmasaba/kesehatan/kontak-darurat', + infoWabahPenyakit: '/darmasaba/kesehatan/info-wabah-penyakit', + keamananLingkungan: '/darmasaba/keamanan/keamanan-lingkungan-pecalang-patwal', + polsekTerdekat: '/darmasaba/keamanan/polsek-terdekat', + kontakDaruratKeamanan: '/darmasaba/keamanan/kontak-darurat', + pencegahanKriminalitas: '/darmasaba/keamanan/pencegahan-kriminalitas', + laporanPublik: '/darmasaba/keamanan/laporan-publik', + tipsKeamanan: '/darmasaba/keamanan/tips-keamanan', + pasarDesa: '/darmasaba/ekonomi/pasar-desa', + lowonganKerjaLokal: '/darmasaba/ekonomi/lowongan-kerja-lokal', + strukturOrganisasi: '/darmasaba/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa', + jumlahPendudukUsiaKerjaYangMenganggurUsia: '/darmasaba/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur', + jumlahPendudukUsiaKerjaYangMenganggurPendidikan: '/darmasaba/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur', + jumlahPendudukMiskin: '/darmasaba/ekonomi/jumlah-penduduk-miskin', + programKemiskinan: '/darmasaba/ekonomi/program-kemiskinan', + sektorUnggulanDesa: '/darmasaba/ekonomi/sektor-unggulan-desa', + demografiPekerjaan: '/darmasaba/ekonomi/demografi-pekerjaan', + }; + + return typeUrlMap[type || ''] || '/darmasaba'; +}; + +export default getDetailUrl; diff --git a/src/app/darmasaba/page.tsx b/src/app/darmasaba/page.tsx index 092b49cb..8e2cf411 100644 --- a/src/app/darmasaba/page.tsx +++ b/src/app/darmasaba/page.tsx @@ -8,23 +8,28 @@ import colors from "@/con/colors"; import SDGS from "./_com/main-page/sdgs"; // import ApiFetch from "@/lib/api-fetch"; -import { Stack } from "@mantine/core"; +import { Box, Stack } from "@mantine/core"; import Apbdes from "./_com/main-page/apbdes"; import Prestasi from "./_com/main-page/prestasi"; +import ScrollToTopButton from "./_com/scrollToTopButton"; export default function Page() { return ( - - - - - - - - - - - + + + + + + + + + + + + + {/* Tombol Scroll ke Atas */} + + ); }