From 83a2dece57baa276db02a2f78afd8f44398c6ff4 Mon Sep 17 00:00:00 2001 From: nico Date: Thu, 30 Apr 2026 15:09:33 +0800 Subject: [PATCH] refactor(kegiatan-desa): redesign public list page to card grid + kategori filter - Remove hero/featured section and tab navigation - Redesign to pecalang-style: 3-col card grid (image, title, desc, Detail button) - Replace tabs with Select dropdown filter by kategori - Search + kategori filter use query params, stay on /semua route - Image hidden when empty (no placeholder) Co-Authored-By: Claude Sonnet 4.6 --- .../desa/kegiatan-desa/_lib/layoutTabs.tsx | 113 ++++------ .../(pages)/desa/kegiatan-desa/semua/page.tsx | 206 ++++++------------ 2 files changed, 107 insertions(+), 212 deletions(-) diff --git a/src/app/darmasaba/(pages)/desa/kegiatan-desa/_lib/layoutTabs.tsx b/src/app/darmasaba/(pages)/desa/kegiatan-desa/_lib/layoutTabs.tsx index 3fe8eb32..fa2e4fc4 100644 --- a/src/app/darmasaba/(pages)/desa/kegiatan-desa/_lib/layoutTabs.tsx +++ b/src/app/darmasaba/(pages)/desa/kegiatan-desa/_lib/layoutTabs.tsx @@ -4,69 +4,45 @@ import stateDashboardKegiatan from '@/app/admin/(dashboard)/_state/desa/kegiatanDesa'; import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto'; import colors from '@/con/colors'; -import { Box, Group, Stack, Tabs, TabsList, TabsTab, Text, TextInput } from '@mantine/core'; +import { Box, Group, Select, Stack, Text, TextInput } from '@mantine/core'; +import { useDebouncedValue } from '@mantine/hooks'; import { IconSearch } from '@tabler/icons-react'; -import { usePathname, useRouter, useSearchParams } from 'next/navigation'; +import { useRouter, useSearchParams } from 'next/navigation'; import React, { useEffect, useState } from 'react'; import { useProxy } from 'valtio/utils'; function LayoutTabsKegiatanDesa({ children }: { children: React.ReactNode }) { const router = useRouter(); - const pathname = usePathname(); const searchParams = useSearchParams(); - const kategoriState = useProxy(stateDashboardKegiatan.kategoriKegiatan); - const activeTab = pathname.split('/').pop() || 'semua'; - const [activeTabState, setActiveTabState] = useState(activeTab); + + const [searchValue, setSearchValue] = useState(searchParams.get('search') || ''); + const [debouncedSearch] = useDebouncedValue(searchValue, 500); useEffect(() => { kategoriState.findMany.load(1, 100); }, []); useEffect(() => { - setActiveTabState(activeTab); - }, [activeTab]); + const params = new URLSearchParams(searchParams.toString()); + if (debouncedSearch) params.set('search', debouncedSearch); + else params.delete('search'); + params.delete('page'); + router.push(`/darmasaba/desa/kegiatan-desa/semua?${params.toString()}`); + }, [debouncedSearch]); - const initialSearch = searchParams.get('search') || ''; - const [searchValue, setSearchValue] = useState(initialSearch); - const [searchTimeout, setSearchTimeout] = useState(null); - - const handleSearchChange = (event: React.ChangeEvent) => { - const value = event.target.value; - setSearchValue(value); - - if (searchTimeout !== null) clearTimeout(searchTimeout); - - const newTimeout = window.setTimeout(() => { - const params = new URLSearchParams(searchParams.toString()); - if (value) params.set('search', value); - else params.delete('search'); - - router.push( - `/darmasaba/desa/kegiatan-desa/${activeTab}${params.toString() ? `?${params.toString()}` : ''}` - ); - }, 500); - - setSearchTimeout(newTimeout); + const handleKategoriChange = (value: string | null) => { + const params = new URLSearchParams(searchParams.toString()); + if (value) params.set('kategori', value); + else params.delete('kategori'); + params.delete('page'); + router.push(`/darmasaba/desa/kegiatan-desa/semua?${params.toString()}`); }; - const tabs = [ - { label: 'Semua', value: 'semua', href: '/darmasaba/desa/kegiatan-desa/semua' }, - ...(kategoriState.findMany.data || []).map((kat: any) => ({ - label: kat.nama, - value: kat.nama.toLowerCase(), - href: `/darmasaba/desa/kegiatan-desa/${kat.nama.toLowerCase()}`, - })), - ]; - - const handleTabChange = (value: string | null) => { - if (!value) return; - const tab = tabs.find((t) => t.value === value); - if (tab) { - const params = new URLSearchParams(searchParams.toString()); - router.push(`${tab.href}${params.toString() ? `?${params.toString()}` : ''}`); - } - }; + const kategoriOptions = (kategoriState.findMany.data || []).map((k: any) => ({ + value: k.nama, + label: k.nama, + })); return ( @@ -75,7 +51,7 @@ function LayoutTabsKegiatanDesa({ children }: { children: React.ReactNode }) { - + Kegiatan Desa Darmasaba @@ -84,43 +60,30 @@ function LayoutTabsKegiatanDesa({ children }: { children: React.ReactNode }) { Temukan berbagai kegiatan dan program yang dilaksanakan Desa Darmasaba - + + +