Fix QC Kak Inno 22 Des

Fix QC Kak Ayu 22 Des
Fix Tampilan Admin Menu Inovasi
This commit is contained in:
2025-12-24 14:36:51 +08:00
parent f0f201c853
commit 50bc54ceca
39 changed files with 1417 additions and 396 deletions

View File

@@ -194,7 +194,7 @@ function ListProgramKemiskinan({ search }: { search: string }) {
{filteredData.length > 0 ? ( {filteredData.length > 0 ? (
filteredData.map((item) => ( filteredData.map((item) => (
<Paper key={item.id} withBorder p="md" radius="sm"> <Paper key={item.id} withBorder p="md" radius="sm">
<Stack gap={4}> <Stack gap={"xs"}>
<Box> <Box>
<Text fz="sm" fw={600} lh={1.4}> <Text fz="sm" fw={600} lh={1.4}>
Judul Program Judul Program

View File

@@ -40,7 +40,7 @@ function DetailAjukanIdeInofativDesa() {
const data = state.findUnique.data; const data = state.findUnique.data;
return ( return (
<Box py={10}> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Tombol Kembali */} {/* Tombol Kembali */}
<Button <Button
variant="subtle" variant="subtle"
@@ -54,7 +54,7 @@ function DetailAjukanIdeInofativDesa() {
{/* Card Utama */} {/* Card Utama */}
<Paper <Paper
withBorder withBorder
w={{ base: "100%", md: "80%", lg: "60%" }} w={{ base: "100%", md: "70%" }}
bg={colors['white-1']} bg={colors['white-1']}
p="lg" p="lg"
radius="md" radius="md"

View File

@@ -17,7 +17,7 @@ import {
Text, Text,
Title Title
} from '@mantine/core'; } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -44,6 +44,7 @@ function AjukanIdeInovatif() {
function ListAjukanIdeInovatif({ search }: { search: string }) { function ListAjukanIdeInovatif({ search }: { search: string }) {
const state = useProxy(ajukanIdeInovatifState) const state = useProxy(ajukanIdeInovatifState)
const router = useRouter() const router = useRouter()
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
const { const {
data, data,
@@ -54,8 +55,8 @@ function ListAjukanIdeInovatif({ search }: { search: string }) {
} = state.findMany; } = state.findMany;
useShallowEffect(() => { useShallowEffect(() => {
load(page, 10, search) load(page, 10, debouncedSearch)
}, [page, search]) }, [page, debouncedSearch])
const filteredData = data || [] const filteredData = data || []

View File

@@ -111,7 +111,7 @@ function EditDigitalSmartVillage() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Header */} {/* Header */}
<Group mb="md"> <Group mb="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md"> <Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">

View File

@@ -49,7 +49,7 @@ function DetailDesaDigital() {
const data = stateDesaDigital.findUnique.data; const data = stateDesaDigital.findUnique.data;
return ( return (
<Box py={10}> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Tombol Kembali */} {/* Tombol Kembali */}
<Button <Button
variant="subtle" variant="subtle"
@@ -63,7 +63,7 @@ function DetailDesaDigital() {
{/* Card Utama */} {/* Card Utama */}
<Paper <Paper
withBorder withBorder
w={{ base: "100%", md: "60%" }} w={{ base: "100%", md: "70%" }}
bg={colors['white-1']} bg={colors['white-1']}
p="lg" p="lg"
radius="md" radius="md"
@@ -77,12 +77,12 @@ function DetailDesaDigital() {
{/* Sub Card Detail */} {/* Sub Card Detail */}
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs"> <Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
<Stack gap="sm"> <Stack gap="sm">
<Box> <Box pl={5}>
<Text fz="lg" fw="bold">Judul</Text> <Text fz="lg" fw="bold">Judul</Text>
<Text fz="md" c="dimmed">{data?.name || '-'}</Text> <Text fz="md" c="dimmed">{data?.name || '-'}</Text>
</Box> </Box>
<Box> <Box pl={5}>
<Text fz="lg" fw="bold">Deskripsi</Text> <Text fz="lg" fw="bold">Deskripsi</Text>
<Text <Text
fz="md" fz="md"

View File

@@ -73,7 +73,7 @@ export default function CreateDesaDigital() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Header dengan tombol kembali */} {/* Header dengan tombol kembali */}
<Group mb="md" align="center"> <Group mb="md" align="center">
<Button <Button

View File

@@ -18,7 +18,7 @@ import {
Text, Text,
Title Title
} from '@mantine/core'; } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -45,28 +45,31 @@ function DesaDigitalSmartVillage() {
function ListDesaDigitalSmartVillage({ search }: { search: string }) { function ListDesaDigitalSmartVillage({ search }: { search: string }) {
const state = useProxy(desaDigitalState); const state = useProxy(desaDigitalState);
const router = useRouter(); const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load } = state.findMany; const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => { useShallowEffect(() => {
load(page, 10, search); load(page, 10, debouncedSearch);
}, [page, search]); }, [page, debouncedSearch]);
const filteredData = data || []; const filteredData = data || [];
if (loading || !data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py="lg">
<Skeleton height={600} radius="md" /> <Skeleton height={600} radius="md" />
</Stack> </Stack>
); );
} }
return ( return (
<Box py={10}> <Box py="lg">
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
<Group justify="space-between" mb="md"> <Group justify="space-between" mb="md">
<Title order={4}>List Desa Digital Smart Village</Title> <Title order={4} lh={1.2}>
List Desa Digital Smart Village
</Title>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color="blue" color="blue"
@@ -78,15 +81,34 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
Tambah Baru Tambah Baru
</Button> </Button>
</Group> </Group>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover> {/* Desktop Table */}
<Box visibleFrom="sm">
<Table
highlightOnHover
miw={0}
style={{
tableLayout: 'fixed',
width: '100%',
}}
>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh style={{ width: '30%' }}>Nama Inovasi</TableTh> <TableTh style={{ width: '30%' }}>
<TableTh style={{ width: '50%' }}> <Text fz="sm" fw={600} lh={1.4}>
Deskripsi Singkat Inovasi Nama Inovasi
</Text>
</TableTh>
<TableTh style={{ width: '50%' }}>
<Text fz="sm" fw={600} lh={1.4}>
Deskripsi Singkat Inovasi
</Text>
</TableTh>
<TableTh style={{ width: '20%' }}>
<Text fz="sm" fw={600} lh={1.4}>
Aksi
</Text>
</TableTh> </TableTh>
<TableTh style={{ width: '20%' }}>Aksi</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
@@ -94,21 +116,18 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
filteredData.map((item) => ( filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd> <TableTd>
<Box w={200}> <Text fz="md" fw={500} lh={1.5} truncate="end">
<Text fw={500} truncate="end" lineClamp={1}> {item.name}
{item.name} </Text>
</Text>
</Box>
</TableTd> </TableTd>
<TableTd> <TableTd>
<Box w={200}> <Text
<Text fz="sm"
fz="sm" c="dimmed"
c="dimmed" lh={1.5}
lineClamp={1} lineClamp={1}
dangerouslySetInnerHTML={{ __html: item.deskripsi }} dangerouslySetInnerHTML={{ __html: item.deskripsi }}
/> />
</Box>
</TableTd> </TableTd>
<TableTd> <TableTd>
<Button <Button
@@ -121,7 +140,9 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
} }
> >
<IconDeviceImac size={20} /> <IconDeviceImac size={20} />
<Text ml={5}>Detail</Text> <Text ml={5} fz="sm" fw={500} lh={1.4}>
Detail
</Text>
</Button> </Button>
</TableTd> </TableTd>
</TableTr> </TableTr>
@@ -129,8 +150,8 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
) : ( ) : (
<TableTr> <TableTr>
<TableTd colSpan={3}> <TableTd colSpan={3}>
<Center py={20}> <Center py="xl">
<Text color="dimmed"> <Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data inovasi digital yang cocok Tidak ada data inovasi digital yang cocok
</Text> </Text>
</Center> </Center>
@@ -140,6 +161,64 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Box> </Box>
{/* Mobile Cards */}
<Box hiddenFrom="sm">
<Stack gap="md">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="md" radius="md">
<Stack gap={4}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Nama Inovasi
</Text>
<Text fz="sm" fw={500} lh={1.5}>
{item.name}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Deskripsi Singkat Inovasi
</Text>
<Box pl={5}>
<Text
fz="sm"
fw={500}
lh={1.5}
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
/>
</Box>
</Box>
<Box>
<Button
variant="light"
color="blue"
fullWidth
onClick={() =>
router.push(
`/admin/inovasi/desa-digital-smart-village/${item.id}`
)
}
>
<IconDeviceImac size={20} />
<Text ml={5} fz="sm" fw={500} lh={1.4}>
Detail
</Text>
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Center py="xl">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data inovasi digital yang cocok
</Text>
</Center>
)}
</Stack>
</Box>
</Paper> </Paper>
<Center> <Center>
<Pagination <Pagination

View File

@@ -123,7 +123,7 @@ function EditInfoTeknologiTepatGuna() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Tombol back + title */} {/* Tombol back + title */}
<Group mb="md"> <Group mb="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md"> <Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">

View File

@@ -40,7 +40,7 @@ function DetailInfoTeknologiTepatGuna() {
const data = stateInfoTekno.findUnique.data const data = stateInfoTekno.findUnique.data
return ( return (
<Box py={10}> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Tombol Kembali */} {/* Tombol Kembali */}
<Button <Button
variant="subtle" variant="subtle"
@@ -54,7 +54,7 @@ function DetailInfoTeknologiTepatGuna() {
{/* Card Utama */} {/* Card Utama */}
<Paper <Paper
withBorder withBorder
w={{ base: "100%", md: "50%" }} w={{ base: "100%", md: "70%" }}
bg={colors['white-1']} bg={colors['white-1']}
p="lg" p="lg"
radius="md" radius="md"

View File

@@ -74,7 +74,7 @@ function CreateInfoTeknologiTepatGuna() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Header */} {/* Header */}
<Group mb="md"> <Group mb="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md"> <Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">

View File

@@ -16,9 +16,9 @@ import {
TableThead, TableThead,
TableTr, TableTr,
Text, Text,
Title Title,
} from '@mantine/core'; } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -45,28 +45,31 @@ function InfoTeknologiTepatGuna() {
function ListInfoTeknologiTepatGuna({ search }: { search: string }) { function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
const state = useProxy(infoTeknoState); const state = useProxy(infoTeknoState);
const router = useRouter(); const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load } = state.findMany; const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => { useShallowEffect(() => {
load(page, 10, search); load(page, 10, debouncedSearch);
}, [page, search]); }, [page, debouncedSearch]);
const filteredData = data || []; const filteredData = data || [];
if (loading || !data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={{ base: 'sm', md: 'lg' }}>
<Skeleton height={600} radius="md" /> <Skeleton height={600} radius="md" />
</Stack> </Stack>
); );
} }
return ( return (
<Box py={10}> <Box py={{ base: 'sm', md: 'lg' }}>
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p={{ base: 'sm', md: 'lg' }} shadow="md" radius="md">
<Group justify="space-between" mb="md"> <Group justify="space-between" mb="md">
<Title order={4}>Daftar Info Teknologi Tepat Guna</Title> <Title order={4} lh={1.2}>
Daftar Info Teknologi Tepat Guna
</Title>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color="blue" color="blue"
@@ -79,40 +82,57 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
</Button> </Button>
</Group> </Group>
<Box style={{ overflowX: 'auto' }}> {/* Desktop Table */}
<Table highlightOnHover> <Box visibleFrom="md">
<Table
highlightOnHover
miw={0}
style={{
tableLayout: 'fixed',
width: '100%',
}}
>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh style={{ width: '30%' }}> <TableTh style={{ width: '30%' }}>
Nama Info Teknologi <Text fz="sm" fw={600} lh={1.4}>
Nama Info Teknologi
</Text>
</TableTh> </TableTh>
<TableTh style={{ width: '50%' }}> <TableTh style={{ width: '50%' }}>
Deskripsi Singkat <Text fz="sm" fw={600} lh={1.4}>
Deskripsi Singkat
</Text>
</TableTh>
<TableTh style={{ width: '20%' }}>
<Text fz="sm" fw={600} lh={1.4}>
Aksi
</Text>
</TableTh> </TableTh>
<TableTh style={{ width: '20%' }}>Aksi</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
{filteredData.length > 0 ? ( {filteredData.length > 0 ? (
filteredData.map((item) => ( filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd style={{ width: '30%' }}> <TableTd>
<Text fw={500} truncate="end" lineClamp={1}> <Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
{item.name} {item.name}
</Text> </Text>
</TableTd> </TableTd>
<TableTd style={{ width: '50%' }}> <TableTd>
<Text <Text
fz="sm"
lh={1.5}
c="dimmed"
lineClamp={1} lineClamp={1}
truncate truncate
fz="sm"
c="dimmed"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: item.deskripsi || '-', __html: item.deskripsi || '-',
}} }}
/> />
</TableTd> </TableTd>
<TableTd style={{ width: '20%' }}> <TableTd>
<Button <Button
size="xs" size="xs"
radius="md" radius="md"
@@ -134,7 +154,7 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
<TableTr> <TableTr>
<TableTd colSpan={3}> <TableTd colSpan={3}>
<Center py={20}> <Center py={20}>
<Text color="dimmed"> <Text fz="sm" c="dimmed" ta="center">
Tidak ada data Info Teknologi yang cocok Tidak ada data Info Teknologi yang cocok
</Text> </Text>
</Center> </Center>
@@ -144,6 +164,61 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Box> </Box>
{/* Mobile Cards */}
<Stack hiddenFrom="md" gap="xs">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="sm" radius="md">
<Stack gap={"xs"}>
<Box>
<Text fz={"sm"} fw={600} lh={1.4}>
Nama Info Teknologi
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.name}
</Text>
</Box>
<Box>
<Text fz={"sm"} fw={600} lh={1.4}>
Deskripsi Singkat
</Text>
<Text
fz="sm"
fw={500}
lh={1.4}
dangerouslySetInnerHTML={{
__html: item.deskripsi || '-',
}}
/>
</Box>
<Group justify="flex-end">
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImac size={16} />}
onClick={() =>
router.push(
`/admin/inovasi/info-teknologi-tepat-guna/${item.id}`,
)
}
>
Detail
</Button>
</Group>
</Stack>
</Paper>
))
) : (
<Center py={20}>
<Text fz="sm" c="dimmed" ta="center">
Tidak ada data Info Teknologi yang cocok
</Text>
</Center>
)}
</Stack>
</Paper> </Paper>
<Center> <Center>
@@ -164,4 +239,4 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
); );
} }
export default InfoTeknologiTepatGuna; export default InfoTeknologiTepatGuna;

View File

@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core'; import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import { IconListDetails, IconUsers } from '@tabler/icons-react'; import { IconListDetails, IconUsers } from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation'; import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
@@ -57,36 +57,76 @@ function LayoutTabsKolaborasi({ children }: { children: React.ReactNode }) {
keepMounted={false} keepMounted={false}
> >
{/* ✅ Scroll horizontal wrapper biar rapi */} {/* ✅ Scroll horizontal wrapper biar rapi */}
<ScrollArea type="auto" offsetScrollbars> <Box visibleFrom='md' pb={10}>
<TabsList <ScrollArea type="auto" offsetScrollbars>
p="sm" <TabsList
style={{ p="sm"
background: "linear-gradient(135deg, #e7ebf7, #f9faff)", style={{
borderRadius: "1rem", background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)", borderRadius: "1rem",
display: "flex", boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
flexWrap: "nowrap", display: "flex",
gap: "0.5rem", flexWrap: "nowrap",
paddingInline: "0.5rem", gap: "0.5rem",
}} paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
> }}
{tabs.map((tab, i) => ( >
<TabsTab {tabs.map((tab, i) => (
key={i} <TabsTab
value={tab.value} key={i}
leftSection={tab.icon} value={tab.value}
style={{ leftSection={tab.icon}
fontWeight: 600, style={{
fontSize: "0.9rem", fontWeight: 600,
transition: "all 0.2s ease", fontSize: "0.9rem",
}} transition: "all 0.2s ease",
> flexShrink: 0, // ✅ jangan mengecil aneh-aneh
{tab.label} }}
</TabsTab> >
))} {tab.label}
</TabsList> </TabsTab>
</ScrollArea> ))}
</TabsList>
</ScrollArea>
</Box>
<Box hiddenFrom='md' pb={10}>
<ScrollArea
type="auto"
offsetScrollbars={false}
w="100%"
>
<TabsList
p="xs" // lebih kecil
style={{
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
borderRadius: "1rem",
display: "flex",
flexWrap: "nowrap",
gap: "0.5rem",
width: "max-content", // ⬅️ kunci
maxWidth: "100%", // ⬅️ penting
}}
>
{tabs.map((tab, i) => (
<TabsTab
key={i}
value={tab.value}
leftSection={tab.icon}
style={{
fontWeight: 600,
fontSize: "0.9rem",
paddingInline: "0.75rem", // ⬅️ lebih ramping
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
}}
>
{tab.label}
</TabsTab>
))}
</TabsList>
</ScrollArea>
</Box>
{tabs.map((tab, i) => ( {tabs.map((tab, i) => (
<TabsPanel <TabsPanel
key={i} key={i}

View File

@@ -1,7 +1,29 @@
'use client'
import React from 'react'; import React from 'react';
import LayoutTabsKolaborasi from './_lib/layoutTabs'; import LayoutTabsKolaborasi from './_lib/layoutTabs';
import { usePathname } from 'next/navigation';
import { Box } from '@mantine/core';
function Layout({ children }: { children: React.ReactNode }) { function Layout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
// Contoh path:
// - /darmasaba/desa/berita/semua → panjang 5 → list
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
const segments = pathname.split('/').filter(Boolean);
const isDetailPage = segments.length >= 5;
if (isDetailPage) {
// Tampilkan tanpa tab menu
return (
<Box>
{children}
</Box>
);
}
return ( return (
<LayoutTabsKolaborasi> <LayoutTabsKolaborasi>
{children} {children}

View File

@@ -118,7 +118,7 @@ function EditKolaborasiInovasi() {
}; };
return ( return (
<Box px={{ base: "sm", md: "lg" }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
<Group mb="md"> <Group mb="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md"> <Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
<IconArrowBack color={colors["blue-button"]} size={24} /> <IconArrowBack color={colors["blue-button"]} size={24} />

View File

@@ -41,7 +41,7 @@ function DetailKolaborasiInovasi() {
const data = kolaborasiState.findUnique.data; const data = kolaborasiState.findUnique.data;
return ( return (
<Box py={10}> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Tombol kembali */} {/* Tombol kembali */}
<Button <Button
variant="subtle" variant="subtle"

View File

@@ -55,7 +55,7 @@ function CreateProgramKreatifDesa() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Back Button */} {/* Back Button */}
<Group mb="md"> <Group mb="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md"> <Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">

View File

@@ -19,6 +19,7 @@ import {
Text, Text,
Title Title
} from '@mantine/core'; } from '@mantine/core';
import { useDebouncedValue } from '@mantine/hooks';
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@@ -45,69 +46,107 @@ function KolaborasiInovasi() {
function ListKolaborasiInovasi({ search }: { search: string }) { function ListKolaborasiInovasi({ search }: { search: string }) {
const listState = useProxy(kolaborasiInovasiState); const listState = useProxy(kolaborasiInovasiState);
const router = useRouter(); const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, loading, page, totalPages, load } = listState.findMany; const { data, loading, page, totalPages, load } = listState.findMany;
useEffect(() => { useEffect(() => {
load(page, 10, search); load(page, 10, debouncedSearch);
}, [page, search]); }, [page, debouncedSearch]);
const filteredData = data || []; const filteredData = data || [];
if (loading || !data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={{ base: 'xs', md: 'md' }}>
<Skeleton height={600} radius="md" /> <Skeleton height={600} radius="md" />
</Stack> </Stack>
); );
} }
return ( return (
<Box py={10}> <Box py={{ base: 'xs', md: 'md' }}>
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p={{ base: 'sm', md: 'lg' }} shadow="md" radius="md">
<Group justify="space-between" mb="md"> <Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
<Title order={4}>Daftar Kolaborasi Inovasi</Title> <Title order={4} lh={1.2}>
Daftar Kolaborasi Inovasi
</Title>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color="blue" color="blue"
variant="light" variant="light"
onClick={() => router.push('/admin/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/create')} onClick={() =>
router.push('/admin/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/create')
}
> >
Tambah Baru Tambah Baru
</Button> </Button>
</Group> </Group>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover> {/* Desktop Table */}
<Box visibleFrom="md">
<Table
highlightOnHover
miw={0}
style={{
tableLayout: 'fixed',
width: '100%',
}}
>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh style={{ width: '5%' }}>No</TableTh> <TableTh style={{ width: '5%' }}>
<TableTh style={{ width: '25%' }}>Nama Kolaborasi Inovasi</TableTh> <Text fz="xs" fw={600} lh={1.4} ta="center">
<TableTh style={{ width: '15%' }}>Tahun</TableTh> No
<TableTh style={{ width: '35%' }}>Deskripsi Singkat</TableTh> </Text>
<TableTh style={{ width: '10%' }}>Aksi</TableTh> </TableTh>
<TableTh style={{ width: '25%' }}>
<Text fz="xs" fw={600} lh={1.4}>
Nama Kolaborasi Inovasi
</Text>
</TableTh>
<TableTh style={{ width: '15%' }}>
<Text fz="xs" fw={600} lh={1.4} ta="center">
Tahun
</Text>
</TableTh>
<TableTh style={{ width: '35%' }}>
<Text fz="xs" fw={600} lh={1.4}>
Deskripsi Singkat
</Text>
</TableTh>
<TableTh style={{ width: '10%' }}>
<Text fz="xs" fw={600} lh={1.4} ta="center">
Aksi
</Text>
</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
{filteredData.length > 0 ? ( {filteredData.length > 0 ? (
filteredData.map((item, index) => ( filteredData.map((item, index) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd>{index + 1}</TableTd> <TableTd ta="center">
<TableTd> <Text fz="sm" fw={500} lh={1.45}>
<Text fw={500} truncate="end" lineClamp={1}> {index + 1}
{item.name}
</Text> </Text>
</TableTd> </TableTd>
<TableTd> <TableTd>
<Text fz="sm" c="dimmed"> <Text fz="sm" fw={500} lh={1.45} truncate="end" lineClamp={1}>
{item.name}
</Text>
</TableTd>
<TableTd ta="center">
<Text fz="sm" fw={500} lh={1.45}>
{item.tahun} {item.tahun}
</Text> </Text>
</TableTd> </TableTd>
<TableTd> <TableTd>
<Text fz="sm" truncate="end" lineClamp={1} c="dimmed"> <Text fz="sm" fw={500} lh={1.45} truncate="end" lineClamp={1}>
{item.slug} {item.slug}
</Text> </Text>
</TableTd> </TableTd>
<TableTd> <TableTd ta="center">
<Button <Button
size="xs" size="xs"
radius="md" radius="md"
@@ -115,7 +154,9 @@ function ListKolaborasiInovasi({ search }: { search: string }) {
color="blue" color="blue"
leftSection={<IconDeviceImac size={16} />} leftSection={<IconDeviceImac size={16} />}
onClick={() => onClick={() =>
router.push(`/admin/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/${item.id}`) router.push(
`/admin/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/${item.id}`
)
} }
> >
Detail Detail
@@ -127,7 +168,9 @@ function ListKolaborasiInovasi({ search }: { search: string }) {
<TableTr> <TableTr>
<TableTd colSpan={5}> <TableTd colSpan={5}>
<Center py={20}> <Center py={20}>
<Text color="dimmed">Tidak ada data kolaborasi inovasi yang tersedia</Text> <Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data kolaborasi inovasi yang tersedia
</Text>
</Center> </Center>
</TableTd> </TableTd>
</TableTr> </TableTr>
@@ -135,23 +178,95 @@ function ListKolaborasiInovasi({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Box> </Box>
{/* Mobile Card View */}
<Box hiddenFrom="md">
<Stack gap="sm">
{filteredData.length > 0 ? (
filteredData.map((item, index) => (
<Paper key={item.id} withBorder radius="md" p="sm">
<Stack gap={"xs"}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
No
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{index + 1}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Nama Kolaborasi Inovasi
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.name}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Tahun
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.tahun}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Deskripsi Singkat
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.slug}
</Text>
</Box>
<Box>
<Button
fullWidth
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImac size={16} />}
onClick={() =>
router.push(
`/admin/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/${item.id}`
)
}
>
Detail
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Center py={20}>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data kolaborasi inovasi yang tersedia
</Text>
</Center>
)}
</Stack>
</Box>
</Paper> </Paper>
<Center>
<Pagination {totalPages > 1 && (
value={page} <Center>
onChange={(newPage) => { <Pagination
load(newPage, 10); value={page}
window.scrollTo({ top: 0, behavior: 'smooth' }); onChange={(newPage) => {
}} load(newPage, 10);
total={totalPages} window.scrollTo({ top: 0, behavior: 'smooth' });
mt="md" }}
mb="md" total={totalPages}
color="blue" mt={{ base: 'sm', md: 'md' }}
radius="md" mb={{ base: 'sm', md: 'md' }}
/> color="blue"
</Center> radius="md"
/>
</Center>
)}
</Box> </Box>
); );
} }
export default KolaborasiInovasi; export default KolaborasiInovasi;

View File

@@ -136,7 +136,7 @@ function EditMitraKolaborasi() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Header */} {/* Header */}
<Group mb="md"> <Group mb="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md"> <Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">

View File

@@ -68,7 +68,7 @@ function CreateMitraKolaborasi() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Back Button + Title */} {/* Back Button + Title */}
<Group mb="md"> <Group mb="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md"> <Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">

View File

@@ -18,7 +18,7 @@ import {
TableThead, TableThead,
TableTr, TableTr,
Text, Text,
Title Title,
} from '@mantine/core'; } from '@mantine/core';
import { IconEdit, IconPlus, IconSearch, IconX } from '@tabler/icons-react'; import { IconEdit, IconPlus, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
@@ -27,6 +27,7 @@ import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header'; import HeaderSearch from '../../../_com/header';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import mitraKolaborasi from '../../../_state/inovasi/mitra-kolaborasi'; import mitraKolaborasi from '../../../_state/inovasi/mitra-kolaborasi';
import { useDebouncedValue } from '@mantine/hooks';
function MitraKolaborasi() { function MitraKolaborasi() {
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
@@ -59,27 +60,34 @@ function ListMitraKolaborasi({ search }: { search: string }) {
} }
}; };
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, loading, page, totalPages, load } = listState.findMany; const { data, loading, page, totalPages, load } = listState.findMany;
useEffect(() => { useEffect(() => {
load(page, 10, search); load(page, 10, debouncedSearch);
}, [page, search]); }, [page, debouncedSearch]);
const filteredData = data || []; const filteredData = data || [];
if (loading || !data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py="lg">
<Skeleton height={600} radius="md" /> <Skeleton height={600} radius="md" />
</Stack> </Stack>
); );
} }
return ( return (
<Box py={10}> <Box py="lg">
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
<Group justify="space-between" mb="md"> <Group justify="space-between" mb="lg">
<Title order={4}>Daftar Mitra Kolaborasi</Title> <Title
order={4}
lh={{ base: 1.2, md: 1.15 }}
>
Daftar Mitra Kolaborasi
</Title>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color="blue" color="blue"
@@ -93,41 +101,82 @@ function ListMitraKolaborasi({ search }: { search: string }) {
Tambah Baru Tambah Baru
</Button> </Button>
</Group> </Group>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover> {/* Desktop Table */}
<Box visibleFrom="md">
<Table
highlightOnHover
miw={0}
style={{
tableLayout: 'fixed',
width: '100%',
}}
>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh style={{ width: '10%' }}>No</TableTh> <TableTh style={{ width: '10%' }}>
<TableTh style={{ width: '30%' }}>Nama Mitra</TableTh> <Text fz={{ base: 'sm', md: 'md' }} fw={600} lh={1.4}>
<TableTh style={{ width: '25%' }}>Image</TableTh> No
<TableTh style={{ width: '15%' }}>Delete</TableTh> </Text>
<TableTh style={{ width: '15%' }}>Edit</TableTh> </TableTh>
<TableTh style={{ width: '30%' }}>
<Text fz={{ base: 'sm', md: 'md' }} fw={600} lh={1.4}>
Nama Mitra
</Text>
</TableTh>
<TableTh style={{ width: '25%' }}>
<Text fz={{ base: 'sm', md: 'md' }} fw={600} lh={1.4}>
Image
</Text>
</TableTh>
<TableTh style={{ width: '15%' }}>
<Text fz={{ base: 'sm', md: 'md' }} fw={600} lh={1.4}>
Delete
</Text>
</TableTh>
<TableTh style={{ width: '15%' }}>
<Text fz={{ base: 'sm', md: 'md' }} fw={600} lh={1.4}>
Edit
</Text>
</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
{filteredData.length > 0 ? ( {filteredData.length > 0 ? (
filteredData.map((item, index) => ( filteredData.map((item, index) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd>{index + 1}</TableTd>
<TableTd> <TableTd>
<Text fw={500} truncate="end" lineClamp={1}> <Text fz={{ base: 'xs', md: 'sm' }} fw={500} lh={1.5}>
{index + 1}
</Text>
</TableTd>
<TableTd>
<Text
fz={{ base: 'sm', md: 'md' }}
fw={500}
lh={1.5}
truncate="end"
lineClamp={1}
>
{item.name} {item.name}
</Text> </Text>
</TableTd> </TableTd>
<TableTd> <TableTd>
<Box <Box w={70} h={70}>
w={70}
h={70}
>
{item.image?.link ? ( {item.image?.link ? (
<Image <Image
loading="lazy" loading="lazy"
src={item.image.link} src={item.image.link}
alt={item.name} alt={item.name}
fit="cover" fit="cover"
radius="sm"
/> />
) : ( ) : (
<Box bg={colors['blue-button']} w="100%" h="100%" /> <Box
bg={colors['blue-button']}
w="100%"
h="100%"
/>
)} )}
</Box> </Box>
</TableTd> </TableTd>
@@ -167,8 +216,8 @@ function ListMitraKolaborasi({ search }: { search: string }) {
) : ( ) : (
<TableTr> <TableTr>
<TableTd colSpan={5}> <TableTd colSpan={5}>
<Center py={20}> <Center py="xl">
<Text color="dimmed"> <Text c="dimmed" fz="sm" ta="center">
Tidak ada data mitra kolaborasi yang tersedia Tidak ada data mitra kolaborasi yang tersedia
</Text> </Text>
</Center> </Center>
@@ -178,7 +227,96 @@ function ListMitraKolaborasi({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Box> </Box>
{/* Mobile Card View */}
<Box hiddenFrom="md">
<Stack gap="xs">
{filteredData.length > 0 ? (
filteredData.map((item, index) => (
<Paper key={item.id} withBorder p="md" radius="md">
<Stack gap={"xs"}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
No
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{index + 1}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Nama Mitra
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.name}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Image
</Text>
<Box w="100%" h={60} mt="xs">
{item.image?.link ? (
<Image
loading="lazy"
src={item.image.link}
alt={item.name}
fit="cover"
radius="sm"
h="100%"
/>
) : (
<Box
bg={colors['blue-button']}
w="100%"
h="100%"
/>
)}
</Box>
</Box>
<Group justify="flex-end" mt="md" gap="xs">
<Button
size="xs"
radius="md"
variant="light"
color="red"
disabled={mitraKolaborasi.delete.loading || !item}
onClick={() => {
setSelectedId(item.id);
setModalHapus(true);
}}
>
<IconX size={16} />
</Button>
<Button
size="xs"
radius="md"
variant="light"
color="green"
disabled={!item}
onClick={() =>
router.push(
`/admin/inovasi/kolaborasi-inovasi/mitra-kolaborasi/${item.id}`
)
}
>
<IconEdit size={16} />
</Button>
</Group>
</Stack>
</Paper>
))
) : (
<Center py="xl">
<Text c="dimmed" fz="sm" ta="center">
Tidak ada data mitra kolaborasi yang tersedia
</Text>
</Center>
)}
</Stack>
</Box>
</Paper> </Paper>
<Center> <Center>
<Pagination <Pagination
value={page} value={page}
@@ -187,14 +325,13 @@ function ListMitraKolaborasi({ search }: { search: string }) {
window.scrollTo({ top: 0, behavior: 'smooth' }); window.scrollTo({ top: 0, behavior: 'smooth' });
}} }}
total={totalPages} total={totalPages}
mt="md" mt="lg"
mb="md" mb="lg"
color="blue" color="blue"
radius="md" radius="md"
/> />
</Center> </Center>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus <ModalKonfirmasiHapus
opened={modalHapus} opened={modalHapus}
onClose={() => setModalHapus(false)} onClose={() => setModalHapus(false)}
@@ -205,4 +342,4 @@ function ListMitraKolaborasi({ search }: { search: string }) {
); );
} }
export default MitraKolaborasi; export default MitraKolaborasi;

View File

@@ -2,6 +2,7 @@
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { import {
Box,
ScrollArea, ScrollArea,
Stack, Stack,
Tabs, Tabs,
@@ -84,36 +85,76 @@ function LayoutTabsLayananOnlineDesa({ children }: { children: React.ReactNode }
keepMounted={false} keepMounted={false}
> >
{/* ✅ Scroll horizontal biar gak overflow */} {/* ✅ Scroll horizontal biar gak overflow */}
<ScrollArea type="auto" offsetScrollbars> <Box visibleFrom='md' pb={10}>
<TabsList <ScrollArea type="auto" offsetScrollbars>
p="sm" <TabsList
style={{ p="sm"
background: "linear-gradient(135deg, #e7ebf7, #f9faff)", style={{
borderRadius: "1rem", background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)", borderRadius: "1rem",
display: "flex", boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
flexWrap: "nowrap", display: "flex",
gap: "0.5rem", flexWrap: "nowrap",
paddingInline: "0.5rem", gap: "0.5rem",
}} paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
> }}
{tabs.map((tab, i) => ( >
<TabsTab {tabs.map((tab, i) => (
key={i} <TabsTab
value={tab.value} key={i}
leftSection={tab.icon} value={tab.value}
style={{ leftSection={tab.icon}
fontWeight: 600, style={{
fontSize: "0.9rem", fontWeight: 600,
transition: "all 0.2s ease", fontSize: "0.9rem",
}} transition: "all 0.2s ease",
> flexShrink: 0, // ✅ jangan mengecil aneh-aneh
{tab.label} }}
</TabsTab> >
))} {tab.label}
</TabsList> </TabsTab>
</ScrollArea> ))}
</TabsList>
</ScrollArea>
</Box>
<Box hiddenFrom='md' pb={10}>
<ScrollArea
type="auto"
offsetScrollbars={false}
w="100%"
>
<TabsList
p="xs" // lebih kecil
style={{
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
borderRadius: "1rem",
display: "flex",
flexWrap: "nowrap",
gap: "0.5rem",
width: "max-content", // ⬅️ kunci
maxWidth: "100%", // ⬅️ penting
}}
>
{tabs.map((tab, i) => (
<TabsTab
key={i}
value={tab.value}
leftSection={tab.icon}
style={{
fontWeight: 600,
fontSize: "0.9rem",
paddingInline: "0.75rem", // ⬅️ lebih ramping
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
}}
>
{tab.label}
</TabsTab>
))}
</TabsList>
</ScrollArea>
</Box>
{tabs.map((tab, i) => ( {tabs.map((tab, i) => (
<TabsPanel <TabsPanel
key={i} key={i}

View File

@@ -42,8 +42,8 @@ function DetailAdministrasiOnline() {
const data = stateAdminOnline.findUnique.data; const data = stateAdminOnline.findUnique.data;
return ( return (
<Box py={10}> <Box px={{ base: 0, md: 'lg' }} py="xs">
<Group justify='space-between' align='center' w={{ base: "100%", md: "50%" }} mb={10}> <Group justify='space-between' align='center' w={{ base: "100%", md: "70%" }} mb={10}>
{/* Tombol Kembali */} {/* Tombol Kembali */}
<Button <Button
variant="subtle" variant="subtle"

View File

@@ -17,7 +17,7 @@ import {
Text, Text,
Title Title
} from '@mantine/core'; } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -44,36 +44,55 @@ function AdministrasiOnline() {
function ListAdministrasiOnline({ search }: { search: string }) { function ListAdministrasiOnline({ search }: { search: string }) {
const state = useProxy(layananonlineDesa.administrasiOnline); const state = useProxy(layananonlineDesa.administrasiOnline);
const router = useRouter(); const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load } = state.findMany; const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => { useShallowEffect(() => {
load(page, 10, search); load(page, 10, debouncedSearch);
}, [page, search]); }, [page, debouncedSearch]);
const filteredData = data || []; const filteredData = data || [];
if (loading || !data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={{ base: 'sm', md: 'md' }}>
<Skeleton height={600} radius="md" /> <Skeleton height={600} radius="md" />
</Stack> </Stack>
); );
} }
return ( return (
<Box py={10}> <Box py={{ base: 'sm', md: 'md' }}>
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
<Title order={4}>Daftar Administrasi Online</Title> <Title order={4} lh={1.2} mb={{ base: 'md', md: 'lg' }}>
Daftar Administrasi Online
</Title>
<Box style={{ overflowX: 'auto' }}> {/* Desktop Table */}
<Table highlightOnHover> <Box visibleFrom="md">
<Table
highlightOnHover
miw={0}
style={{
tableLayout: 'fixed',
width: '100%',
}}
>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh style={{ width: '25%' }}>Nama</TableTh> <TableTh style={{ width: '25%' }}>
<TableTh style={{ width: '25%' }}>Alamat</TableTh> <Text fz="sm" fw={600} lh={1.4}>Nama</Text>
<TableTh style={{ width: '20%' }}>Nomor Telepon</TableTh> </TableTh>
<TableTh style={{ width: '15%' }}>Aksi</TableTh> <TableTh style={{ width: '25%' }}>
<Text fz="sm" fw={600} lh={1.4}>Alamat</Text>
</TableTh>
<TableTh style={{ width: '20%' }}>
<Text fz="sm" fw={600} lh={1.4}>Nomor Telepon</Text>
</TableTh>
<TableTh style={{ width: '15%' }}>
<Text fz="sm" fw={600} lh={1.4}>Aksi</Text>
</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
@@ -81,17 +100,17 @@ function ListAdministrasiOnline({ search }: { search: string }) {
filteredData.map((item) => ( filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd> <TableTd>
<Text fw={500} truncate="end" lineClamp={1}> <Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
{item.name} {item.name}
</Text> </Text>
</TableTd> </TableTd>
<TableTd> <TableTd>
<Text fz="sm" c="dimmed" lineClamp={1}> <Text fz="sm" c="gray.7" lh={1.5} lineClamp={1}>
{item.alamat} {item.alamat}
</Text> </Text>
</TableTd> </TableTd>
<TableTd> <TableTd>
<Text fz="sm" c="dimmed" lineClamp={1}> <Text fz="sm" c="gray.7" lh={1.5} lineClamp={1}>
{item.nomorTelepon || '-'} {item.nomorTelepon || '-'}
</Text> </Text>
</TableTd> </TableTd>
@@ -116,8 +135,10 @@ function ListAdministrasiOnline({ search }: { search: string }) {
) : ( ) : (
<TableTr> <TableTr>
<TableTd colSpan={4}> <TableTd colSpan={4}>
<Center py={20}> <Center py={24}>
<Text color="dimmed">Tidak ada data administrasi online yang cocok</Text> <Text c="gray.6" fz="sm" lh={1.4}>
Tidak ada data administrasi online yang cocok
</Text>
</Center> </Center>
</TableTd> </TableTd>
</TableTr> </TableTr>
@@ -125,6 +146,55 @@ function ListAdministrasiOnline({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Box> </Box>
{/* Mobile Cards */}
<Box hiddenFrom="md">
<Stack gap="md">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} p="md" withBorder>
<Stack gap={"xs"}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Nama</Text>
<Text fz="sm" fw={500} lh={1.5}>{item.name}</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Alamat</Text>
<Text fz="sm" fw={500} c="gray.7" lh={1.5}>{item.alamat}</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Nomor Telepon</Text>
<Text fz="sm" fw={500} c="gray.7" lh={1.5}>{item.nomorTelepon || '-'}</Text>
</Box>
<Box>
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImac size={16} />}
onClick={() =>
router.push(
`/admin/inovasi/layanan-online-desa/administrasi-online/${item.id}`
)
}
fullWidth
>
Detail
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Center py={24}>
<Text c="gray.6" fz="sm" lh={1.4}>
Tidak ada data administrasi online yang cocok
</Text>
</Center>
)}
</Stack>
</Box>
</Paper> </Paper>
<Center> <Center>

View File

@@ -90,7 +90,7 @@ function EditJenisLayanan() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Header */} {/* Header */}
<Group mb="md"> <Group mb="md">
<Button <Button

View File

@@ -40,7 +40,7 @@ function DetailJenisLayanan() {
const data = state.findUnique.data const data = state.findUnique.data
return ( return (
<Box py={10}> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Tombol kembali */} {/* Tombol kembali */}
<Button <Button
variant="subtle" variant="subtle"
@@ -54,7 +54,7 @@ function DetailJenisLayanan() {
{/* Card utama */} {/* Card utama */}
<Paper <Paper
withBorder withBorder
w={{ base: "100%", md: "60%" }} w={{ base: "100%", md: "70%" }}
bg={colors['white-1']} bg={colors['white-1']}
p="lg" p="lg"
radius="md" radius="md"

View File

@@ -50,7 +50,7 @@ function CreateJenisLayanan() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Header dengan tombol back */} {/* Header dengan tombol back */}
<Group mb="md"> <Group mb="md">
<Button <Button

View File

@@ -16,9 +16,10 @@ import {
TableThead, TableThead,
TableTr, TableTr,
Text, Text,
Title Title,
useMantineTheme
} from '@mantine/core'; } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -45,28 +46,35 @@ function JenisLayanan() {
function ListJenisLayanan({ search }: { search: string }) { function ListJenisLayanan({ search }: { search: string }) {
const stateList = useProxy(layananonlineDesa.jenisLayanan); const stateList = useProxy(layananonlineDesa.jenisLayanan);
const router = useRouter(); const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load } = stateList.findMany; const { data, page, totalPages, loading, load } = stateList.findMany;
useShallowEffect(() => { useShallowEffect(() => {
load(page, 10, search); load(page, 10, debouncedSearch);
}, [page, search]); }, [page, debouncedSearch]);
const filteredData = data || []; const filteredData = data || [];
const theme = useMantineTheme();
if (loading || !data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py="lg">
<Skeleton height={600} radius="md" /> <Skeleton height={600} radius="md" />
</Stack> </Stack>
); );
} }
return ( return (
<Box py={10}> <Stack py="lg" gap="xl">
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
<Group justify="space-between" mb="md"> <Group justify="space-between" mb="md">
<Title order={4}>Daftar Jenis Layanan</Title> <Title
order={4}
lh={{ base: 1.2, md: 1.15 }}
>
Daftar Jenis Layanan
</Title>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color="blue" color="blue"
@@ -80,13 +88,34 @@ function ListJenisLayanan({ search }: { search: string }) {
Tambah Baru Tambah Baru
</Button> </Button>
</Group> </Group>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover> {/* Desktop Table */}
<Box visibleFrom="md">
<Table
highlightOnHover
miw={0}
style={{
tableLayout: 'fixed',
width: '100%',
}}
>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh style={{ width: '30%' }}>Nama Jenis Layanan</TableTh> <TableTh style={{ width: '30%' }}>
<TableTh style={{ width: '40%' }}>Deskripsi</TableTh> <Text fz="sm" fw={600} lh={1.4} c={theme.black}>
<TableTh style={{ width: '15%' }}>Aksi</TableTh> Nama Jenis Layanan
</Text>
</TableTh>
<TableTh style={{ width: '55%' }}>
<Text fz="sm" fw={600} lh={1.4} c={theme.black}>
Deskripsi
</Text>
</TableTh>
<TableTh style={{ width: '15%' }}>
<Text fz="sm" fw={600} lh={1.4} ta="center" c={theme.black}>
Aksi
</Text>
</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
@@ -94,16 +123,29 @@ function ListJenisLayanan({ search }: { search: string }) {
filteredData.map((item) => ( filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd style={{ width: '30%' }}> <TableTd style={{ width: '30%' }}>
<Text fw={500} truncate="end" lineClamp={1}> <Text
fz="md"
fw={500}
lh={1.5}
c={theme.black}
truncate="end"
lineClamp={1}
>
{item.nama} {item.nama}
</Text> </Text>
</TableTd> </TableTd>
<TableTd style={{ width: '40%' }}> <TableTd style={{ width: '55%' }}>
<Text fz="sm" c="dimmed" lineClamp={2}> <Text
fz="sm"
fw={500}
lh={1.5}
c={theme.black}
lineClamp={2}
>
{item.deskripsi || '-'} {item.deskripsi || '-'}
</Text> </Text>
</TableTd> </TableTd>
<TableTd style={{ width: '15%' }}> <TableTd style={{ width: '15%' }} ta="center">
<Button <Button
size="xs" size="xs"
radius="md" radius="md"
@@ -124,8 +166,8 @@ function ListJenisLayanan({ search }: { search: string }) {
) : ( ) : (
<TableTr> <TableTr>
<TableTd colSpan={3}> <TableTd colSpan={3}>
<Center py={20}> <Center py="xl">
<Text color="dimmed"> <Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada jenis layanan yang cocok Tidak ada jenis layanan yang cocok
</Text> </Text>
</Center> </Center>
@@ -135,22 +177,76 @@ function ListJenisLayanan({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Box> </Box>
{/* Mobile Cards */}
<Box hiddenFrom="md">
<Stack gap="sm">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder radius="md" p="md">
<Stack gap="xs">
<Box pl={5}>
<Text fz="sm" fw={600} lh={1.4} c={theme.black}>
Nama Jenis Layanan
</Text>
<Text fz="sm" fw={500} lh={1.5} c={theme.black}>
{item.nama}
</Text>
</Box>
<Box pl={5}>
<Text fz="sm" fw={600} lh={1.4} c={theme.black}>
Deskripsi
</Text>
<Text fz="sm" fw={500} lh={1.5} c={theme.black}>
{item.deskripsi || '-'}
</Text>
</Box>
<Group justify="flex-end" mt="xs">
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImac size={16} />}
onClick={() =>
router.push(
`/admin/inovasi/layanan-online-desa/jenis-layanan/${item.id}`
)
}
>
Detail
</Button>
</Group>
</Stack>
</Paper>
))
) : (
<Center py="xl">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada jenis layanan yang cocok
</Text>
</Center>
)}
</Stack>
</Box>
</Paper> </Paper>
<Center>
<Pagination {totalPages > 1 && (
value={page} <Center>
onChange={(newPage) => { <Pagination
load(newPage, 10); value={page}
window.scrollTo({ top: 0, behavior: 'smooth' }); onChange={(newPage) => {
}} load(newPage, 10);
total={totalPages} window.scrollTo({ top: 0, behavior: 'smooth' });
mt="md" }}
mb="md" total={totalPages}
color="blue" mt="md"
radius="md" color="blue"
/> radius="md"
</Center> />
</Box> </Center>
)}
</Stack>
); );
} }

View File

@@ -105,7 +105,7 @@ function EditJenisPengaduan() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Header */} {/* Header */}
<Group mb="md"> <Group mb="md">
<Button <Button

View File

@@ -47,7 +47,7 @@ function CreateJenisPengaduan() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Header */} {/* Header */}
<Group mb="md"> <Group mb="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md"> <Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">

View File

@@ -18,7 +18,7 @@ import {
Text, Text,
Title Title
} from '@mantine/core'; } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react'; import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -48,12 +48,13 @@ function ListJenisPengaduan({ search }: { search: string }) {
const router = useRouter(); const router = useRouter();
const [modalHapus, setModalHapus] = useState(false); const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null); const [selectedId, setSelectedId] = useState<string | null>(null);
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load, } = state.findMany; const { data, page, totalPages, loading, load } = state.findMany;
useShallowEffect(() => { useShallowEffect(() => {
load(page, 10, search); load(page, 10, debouncedSearch);
}, [page, search]); }, [page, debouncedSearch]);
const handleHapus = async () => { const handleHapus = async () => {
if (selectedId) { if (selectedId) {
@@ -63,21 +64,23 @@ function ListJenisPengaduan({ search }: { search: string }) {
} }
}; };
const filteredData = data || [] const filteredData = data || [];
if (loading || !data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={{ base: 'md', md: 'lg' }}>
<Skeleton height={500} radius="md" /> <Skeleton height={500} radius="md" />
</Stack> </Stack>
); );
} }
return ( return (
<Box py={10}> <Box py={{ base: 'md', md: 'lg' }}>
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
<Group justify="space-between" mb="md"> <Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
<Title order={4}>Daftar Jenis Pengaduan</Title> <Title order={4} lh={1.2}>
Daftar Jenis Pengaduan
</Title>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color="blue" color="blue"
@@ -92,13 +95,33 @@ function ListJenisPengaduan({ search }: { search: string }) {
</Button> </Button>
</Group> </Group>
<Box style={{ overflowX: 'auto' }}> {/* Desktop Table */}
<Table highlightOnHover> <Box visibleFrom="md">
<Table
highlightOnHover
miw={0}
style={{
tableLayout: 'fixed',
width: '100%',
}}
>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh style={{ width: '60%' }}>Nama Jenis Pengaduan</TableTh> <TableTh style={{ width: '60%' }}>
<TableTh style={{ width: '20%' }}>Edit</TableTh> <Text fz="sm" fw={600} lh={1.4} c="dimmed">
<TableTh style={{ width: '20%' }}>Hapus</TableTh> Nama Jenis Pengaduan
</Text>
</TableTh>
<TableTh style={{ width: '20%' }}>
<Text fz="sm" fw={600} lh={1.4} c="dimmed">
Edit
</Text>
</TableTh>
<TableTh style={{ width: '20%' }}>
<Text fz="sm" fw={600} lh={1.4} c="dimmed">
Hapus
</Text>
</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
@@ -106,7 +129,7 @@ function ListJenisPengaduan({ search }: { search: string }) {
filteredData.map((item) => ( filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd> <TableTd>
<Text fw={500} truncate="end" lineClamp={1}> <Text fz="md" fw={500} lh={1.5} truncate="end">
{item.nama} {item.nama}
</Text> </Text>
</TableTd> </TableTd>
@@ -147,7 +170,7 @@ function ListJenisPengaduan({ search }: { search: string }) {
<TableTr> <TableTr>
<TableTd colSpan={3}> <TableTd colSpan={3}>
<Center py={20}> <Center py={20}>
<Text color="dimmed"> <Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada jenis pengaduan yang cocok Tidak ada jenis pengaduan yang cocok
</Text> </Text>
</Center> </Center>
@@ -157,6 +180,63 @@ function ListJenisPengaduan({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Box> </Box>
{/* Mobile Card View */}
<Box hiddenFrom="md">
<Stack gap="md">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="md" radius="md">
<Stack gap={"xs"}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Nama Jenis Pengaduan
</Text>
<Text fz="sm" fw={500} lh={1.45}>
{item.nama}
</Text>
</Box>
<Group justify="flex-end" mt="xs">
<Button
size="xs"
radius="md"
variant="light"
color="green"
leftSection={<IconEdit size={16} />}
onClick={() =>
router.push(
`/admin/inovasi/layanan-online-desa/jenis-pengaduan/${item.id}`
)
}
>
Edit
</Button>
<Button
size="xs"
radius="md"
variant="light"
color="red"
leftSection={<IconTrash size={16} />}
onClick={() => {
setSelectedId(item.id);
setModalHapus(true);
}}
>
Hapus
</Button>
</Group>
</Stack>
</Paper>
))
) : (
<Center py={20}>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada jenis pengaduan yang cocok
</Text>
</Center>
)}
</Stack>
</Box>
</Paper> </Paper>
<Center> <Center>
@@ -167,14 +247,13 @@ function ListJenisPengaduan({ search }: { search: string }) {
window.scrollTo({ top: 0, behavior: 'smooth' }); window.scrollTo({ top: 0, behavior: 'smooth' });
}} }}
total={totalPages} total={totalPages}
mt="md" mt={{ base: 'sm', md: 'md' }}
mb="md" mb={{ base: 'sm', md: 'md' }}
color="blue" color="blue"
radius="md" radius="md"
/> />
</Center> </Center>
{/* Modal Hapus */}
<ModalKonfirmasiHapus <ModalKonfirmasiHapus
opened={modalHapus} opened={modalHapus}
onClose={() => setModalHapus(false)} onClose={() => setModalHapus(false)}
@@ -185,4 +264,4 @@ function ListJenisPengaduan({ search }: { search: string }) {
); );
} }
export default JenisPengaduan; export default JenisPengaduan;

View File

@@ -1,7 +1,28 @@
'use client' 'use client'
import { Box } from "@mantine/core";
import LayoutTabsLayananOnlineDesa from "./_lib/layoutTabs"; import LayoutTabsLayananOnlineDesa from "./_lib/layoutTabs";
import { usePathname } from "next/navigation";
function Layout({ children }: { children: React.ReactNode }) { function Layout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
// Contoh path:
// - /darmasaba/desa/berita/semua → panjang 5 → list
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
const segments = pathname.split('/').filter(Boolean);
const isDetailPage = segments.length >= 5;
if (isDetailPage) {
// Tampilkan tanpa tab menu
return (
<Box>
{children}
</Box>
);
}
return ( return (
<LayoutTabsLayananOnlineDesa> <LayoutTabsLayananOnlineDesa>
{children} {children}

View File

@@ -41,7 +41,7 @@ function DetailPengaduanMasyarakat() {
const data = pengaduanState.pengaduanMasyarakat.findUnique.data; const data = pengaduanState.pengaduanMasyarakat.findUnique.data;
return ( return (
<Box py={10}> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Tombol Kembali */} {/* Tombol Kembali */}
<Button <Button
variant="subtle" variant="subtle"
@@ -55,7 +55,7 @@ function DetailPengaduanMasyarakat() {
{/* Card Detail */} {/* Card Detail */}
<Paper <Paper
withBorder withBorder
w={{ base: "100%", md: "60%" }} w={{ base: "100%", md: "70%" }}
bg={colors['white-1']} bg={colors['white-1']}
p="lg" p="lg"
radius="md" radius="md"

View File

@@ -19,8 +19,7 @@ import {
Title Title
} from '@mantine/core'; } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { useShallowEffect } from '@mantine/hooks';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
@@ -46,6 +45,7 @@ function PengaduanMasyarakat() {
function ListPengaduanMasyarakat({ search }: { search: string }) { function ListPengaduanMasyarakat({ search }: { search: string }) {
const listState = useProxy(layananonlineDesa.pengaduanMasyarakat); const listState = useProxy(layananonlineDesa.pengaduanMasyarakat);
const router = useRouter(); const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { const {
data, data,
page, page,
@@ -55,56 +55,84 @@ function ListPengaduanMasyarakat({ search }: { search: string }) {
} = listState.findMany; } = listState.findMany;
useShallowEffect(() => { useShallowEffect(() => {
load(page, 10, search); load(page, 10, debouncedSearch);
}, [page, search]); }, [page, debouncedSearch]);
const filteredData = data || [] const filteredData = data || [];
if (loading || !data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={{ base: 'sm', md: 'md' }}>
<Skeleton height={600} radius="md" /> <Skeleton height={600} radius="md" />
</Stack> </Stack>
); );
} }
return ( return (
<Box py={10}> <Box py={{ base: 'sm', md: 'md' }}>
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
<Group justify="space-between" mb="md"> {/* Section Header */}
<Title order={4}>Daftar Pengaduan Masyarakat</Title> <Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
<Title order={4} lh={1.2}>
Daftar Pengaduan Masyarakat
</Title>
</Group> </Group>
<Box style={{ overflowX: "auto" }}>
<Table highlightOnHover> {/* Desktop Table */}
<Box visibleFrom="md">
<Table
highlightOnHover
miw={0}
style={{
tableLayout: 'fixed',
width: '100%',
}}
>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh style={{ width: '25%' }}>Nama</TableTh> <TableTh style={{ width: '25%' }}>
<TableTh style={{ width: '25%' }}>Email</TableTh> <Text fz="sm" fw={600} lh={1.4}>Nama</Text>
<TableTh style={{ width: '20%' }}>Nomor Telepon</TableTh> </TableTh>
<TableTh style={{ width: '15%' }}>Aksi</TableTh> <TableTh style={{ width: '25%' }}>
<Text fz="sm" fw={600} lh={1.4}>Email</Text>
</TableTh>
<TableTh style={{ width: '20%' }}>
<Text fz="sm" fw={600} lh={1.4}>Nomor Telepon</Text>
</TableTh>
<TableTh style={{ width: '15%' }}>
<Text fz="sm" fw={600} lh={1.4}>Aksi</Text>
</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
{filteredData.length > 0 ? ( {filteredData.length > 0 ? (
filteredData.map((item) => ( filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd style={{ width: '25%' }}> <TableTd>
<Text fw={500} truncate="end" lineClamp={1}>{item.name}</Text> <Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
{item.name}
</Text>
</TableTd> </TableTd>
<TableTd style={{ width: '25%' }}> <TableTd>
<Text fz="sm" c="dimmed" truncate lineClamp={1}>{item.email}</Text> <Text fz="sm" c="dimmed" lh={1.5} truncate lineClamp={1}>
{item.email}
</Text>
</TableTd> </TableTd>
<TableTd style={{ width: '20%' }}> <TableTd>
<Text fz="sm" c="dimmed" truncate lineClamp={1}>{item.nomorTelepon}</Text> <Text fz="sm" c="dimmed" lh={1.5} truncate lineClamp={1}>
{item.nomorTelepon}
</Text>
</TableTd> </TableTd>
<TableTd style={{ width: '15%' }}> <TableTd>
<Button <Button
size="xs" size="xs"
radius="md" radius="md"
variant="light" variant="light"
color="blue" color="blue"
leftSection={<IconDeviceImac size={16} />} leftSection={<IconDeviceImac size={16} />}
onClick={() => router.push(`/admin/inovasi/layanan-online-desa/pengaduan-masyarakat/${item.id}`)} onClick={() =>
router.push(`/admin/inovasi/layanan-online-desa/pengaduan-masyarakat/${item.id}`)
}
> >
Detail Detail
</Button> </Button>
@@ -114,8 +142,10 @@ function ListPengaduanMasyarakat({ search }: { search: string }) {
) : ( ) : (
<TableTr> <TableTr>
<TableTd colSpan={4}> <TableTd colSpan={4}>
<Center py={20}> <Center py={{ base: 'sm', md: 'lg' }}>
<Text color="dimmed">Tidak ada data pengaduan yang cocok</Text> <Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data pengaduan yang cocok
</Text>
</Center> </Center>
</TableTd> </TableTd>
</TableTr> </TableTr>
@@ -123,7 +153,56 @@ function ListPengaduanMasyarakat({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Box> </Box>
{/* Mobile Cards */}
<Box hiddenFrom="md">
<Stack gap="xs">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="sm" radius="md">
<Stack gap={'xs'}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Nama</Text>
<Text fz="sm" fw={500} lh={1.45}>{item.name}</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Email</Text>
<Text fz="sm" c="dimmed" fw={500} lh={1.45}>{item.email}</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Nomor Telepon</Text>
<Text fz="sm" c="dimmed" fw={500} lh={1.45}>{item.nomorTelepon}</Text>
</Box>
<Box>
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImac size={16} />}
onClick={() =>
router.push(`/admin/inovasi/layanan-online-desa/pengaduan-masyarakat/${item.id}`)
}
fullWidth
>
Detail
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Center py="lg">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data pengaduan yang cocok
</Text>
</Center>
)}
</Stack>
</Box>
</Paper> </Paper>
{/* Pagination */}
<Center> <Center>
<Pagination <Pagination
value={page} value={page}
@@ -142,4 +221,4 @@ function ListPengaduanMasyarakat({ search }: { search: string }) {
); );
} }
export default PengaduanMasyarakat; export default PengaduanMasyarakat;

View File

@@ -160,7 +160,7 @@ function EditProgramKreatifDesa() {
}; };
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
<Group mb="md"> <Group mb="md">
<Button <Button
variant="subtle" variant="subtle"

View File

@@ -41,7 +41,7 @@ function DetailProgramKreatifDesa() {
const data = stateProgramKreatif.findUnique.data const data = stateProgramKreatif.findUnique.data
return ( return (
<Box py={10}> <Box px={{ base: 0, md: 'lg' }} py="xs">
<Button <Button
variant="subtle" variant="subtle"
onClick={() => router.back()} onClick={() => router.back()}
@@ -53,7 +53,7 @@ function DetailProgramKreatifDesa() {
<Paper <Paper
withBorder withBorder
w={{ base: "100%", md: "50%" }} w={{ base: "100%", md: "70%" }}
bg={colors['white-1']} bg={colors['white-1']}
p="lg" p="lg"
radius="md" radius="md"

View File

@@ -52,7 +52,7 @@ function CreateProgramKreatifDesa() {
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 0, md: 'lg' }} py="xs">
{/* Tombol kembali */} {/* Tombol kembali */}
<Group mb="md"> <Group mb="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md"> <Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">

View File

@@ -18,7 +18,7 @@ import {
TableThead, TableThead,
TableTr, TableTr,
Text, Text,
Title Title,
} from '@mantine/core'; } from '@mantine/core';
import { import {
IconCash, IconCash,
@@ -50,9 +50,10 @@ import React, { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../_com/header'; import HeaderSearch from '../../_com/header';
import programKreatifState from '../../_state/inovasi/program-kreatif'; import programKreatifState from '../../_state/inovasi/program-kreatif';
import { useDebouncedValue } from '@mantine/hooks';
function ProgramKreatifDesa() { function ProgramKreatifDesa() {
const [search, setSearch] = useState(""); const [search, setSearch] = useState('');
return ( return (
<Box> <Box>
<HeaderSearch <HeaderSearch
@@ -71,12 +72,13 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
const listState = useProxy(programKreatifState); const listState = useProxy(programKreatifState);
const { data, loading, page, totalPages, load } = listState.findMany; const { data, loading, page, totalPages, load } = listState.findMany;
const router = useRouter(); const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
useEffect(() => { useEffect(() => {
load(page, 10, search); load(page, 10, debouncedSearch);
}, [page, search]); }, [page, debouncedSearch]);
const filteredData = data || [] const filteredData = data || [];
const iconMap: Record<string, React.FC<any>> = { const iconMap: Record<string, React.FC<any>> = {
ekowisata: IconLeaf, ekowisata: IconLeaf,
@@ -103,7 +105,7 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
if (loading || !data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={{ base: 'sm', md: 'xl' }}>
<Skeleton height={650} radius="md" /> <Skeleton height={650} radius="md" />
</Stack> </Stack>
); );
@@ -111,35 +113,44 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
if (data.length === 0) { if (data.length === 0) {
return ( return (
<Box py={10}> <Box py={{ base: 'sm', md: 'xl' }}>
<Paper p="md" radius="md" shadow="sm" withBorder> <Paper p="lg" radius="md" shadow="sm" withBorder>
<Stack> <Stack gap="xl">
<Group justify="space-between" mb="md"> <Group justify="space-between" wrap="nowrap">
<Title order={4}>Daftar Program Kreatif Desa</Title> <Title order={4} lh={1.15}>
Daftar Program Kreatif Desa
</Title>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color="blue" color="blue"
variant="light" variant="light"
onClick={() => onClick={() =>
router.push( router.push('/admin/inovasi/program-kreatif-desa/create')
'/admin/inovasi/program-kreatif-desa/create'
)
} }
> >
Tambah Baru Tambah Baru
</Button> </Button>
</Group> </Group>
<Table highlightOnHover striped>
<TableThead> <Box visibleFrom="md">
<TableTr> <Table
<TableTh>No</TableTh> highlightOnHover
<TableTh>Nama Program Kreatif Desa</TableTh> miw={0}
<TableTh>Deskripsi Singkat</TableTh> striped
<TableTh>Ikon</TableTh> style={{ tableLayout: 'fixed', width: '100%' }}
<TableTh>Detail</TableTh> >
</TableTr> <TableThead>
</TableThead> <TableTr>
</Table> <TableTh style={{ width: '5%', textAlign: 'center' }}>No</TableTh>
<TableTh style={{ width: '20%' }}>Nama Program Kreatif Desa</TableTh>
<TableTh style={{ width: '35%' }}>Deskripsi Singkat</TableTh>
<TableTh style={{ width: '10%', textAlign: 'center' }}>Ikon</TableTh>
<TableTh style={{ width: '15%', textAlign: 'center' }}>Detail</TableTh>
</TableTr>
</TableThead>
</Table>
</Box>
<Text ta="center" c="dimmed" py="lg"> <Text ta="center" c="dimmed" py="lg">
Tidak ada data program kreatif desa yang tersedia Tidak ada data program kreatif desa yang tersedia
</Text> </Text>
@@ -150,7 +161,7 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
} }
return ( return (
<Box py={10}> <Box py={{ base: 'sm', md: 'xl' }}>
<Paper <Paper
withBorder withBorder
bg={colors['white-1']} bg={colors['white-1']}
@@ -159,58 +170,83 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
radius="md" radius="md"
h={{ base: 'auto', md: 650 }} h={{ base: 'auto', md: 650 }}
> >
<Group justify="space-between" mb="md"> <Group justify="space-between" mb="xl" wrap="nowrap">
<Title order={4}>Daftar Program Kreatif Desa</Title> <Title order={4} lh={1.15}>
Daftar Program Kreatif Desa
</Title>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color="blue" color="blue"
variant="light" variant="light"
onClick={() => onClick={() =>
router.push( router.push('/admin/inovasi/program-kreatif-desa/create')
'/admin/inovasi/program-kreatif-desa/create'
)
} }
> >
Tambah Baru Tambah Baru
</Button> </Button>
</Group> </Group>
<Box style={{ overflowY: 'auto' }}>
<Table highlightOnHover striped > {/* Desktop Table */}
<Box visibleFrom="md" style={{ height: '100%', overflowY: 'auto' }}>
<Table
highlightOnHover
miw={0}
striped
style={{ tableLayout: 'fixed', width: '100%' }}
>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh style={{ width: '5%', textAlign: 'center' }}>No</TableTh> <TableTh style={{ width: '5%', textAlign: 'center' }}>
<TableTh style={{ width: '20%' }}> <Text fz="xs" fw={600} lh={1.2}>
<Text lineClamp={1} fw={"bold"} fz="sm">Nama Program Kreatif Desa</Text> No
</Text>
</TableTh>
<TableTh style={{ width: '20%' }}>
<Text fz="xs" fw={600} lh={1.2}>
Nama Program Kreatif Desa
</Text>
</TableTh>
<TableTh style={{ width: '35%' }}>
<Text fz="xs" fw={600} lh={1.2}>
Deskripsi Singkat
</Text>
</TableTh>
<TableTh style={{ width: '10%', textAlign: 'center' }}>
<Text fz="xs" fw={600} lh={1.2}>
Ikon
</Text>
</TableTh>
<TableTh style={{ width: '15%', textAlign: 'center' }}>
<Text fz="xs" fw={600} lh={1.2}>
Detail
</Text>
</TableTh> </TableTh>
<TableTh style={{ width: '35%' }}>Deskripsi Singkat</TableTh>
<TableTh style={{ width: '10%', textAlign: 'center' }}>Ikon</TableTh>
<TableTh style={{ width: '15%', textAlign: 'center' }}>Detail</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
{filteredData.map((item, index) => ( {filteredData.map((item, index) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd style={{ width: '5%', textAlign: 'center' }}>{index + 1}</TableTd> <TableTd ta="center" fz="sm" fw={500} lh={1.45}>
<TableTd style={{ width: '20%', wordWrap: 'break-word' }}> {index + 1}
<Box w={200}>
<Text fw={500} lineClamp={1}>{item.name}</Text>
</Box>
</TableTd> </TableTd>
<TableTd style={{ width: '35%', wordWrap: 'break-word' }}> <TableTd>
<Box w={150}> <Text fz="sm" fw={500} lh={1.45} lineClamp={1}>
<Text fz="sm" c="dimmed" lineClamp={1}> {item.name}
{item.slug} </Text>
</Text>
</Box>
</TableTd> </TableTd>
<TableTd style={{ width: '10%', textAlign: 'center' }}> <TableTd>
<Text fz="sm" c="dimmed" lh={1.45} lineClamp={1}>
{item.slug}
</Text>
</TableTd>
<TableTd ta="center">
{iconMap[item.icon] && ( {iconMap[item.icon] && (
<Box title={item.icon}> <Box title={item.icon}>
{React.createElement(iconMap[item.icon], { size: 24 })} {React.createElement(iconMap[item.icon], { size: 24 })}
</Box> </Box>
)} )}
</TableTd> </TableTd>
<TableTd style={{ width: '15%', textAlign: 'center' }}> <TableTd ta="center">
<Button <Button
size="xs" size="xs"
radius="md" radius="md"
@@ -221,7 +257,9 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
} }
> >
<IconDeviceImac size={18} /> <IconDeviceImac size={18} />
<Text ml={6}>Detail</Text> <Text ml={6} fz="sm" fw={500}>
Detail
</Text>
</Button> </Button>
</TableTd> </TableTd>
</TableTr> </TableTr>
@@ -229,7 +267,72 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Box> </Box>
{/* Mobile Cards */}
<Box hiddenFrom="md">
<Stack gap="md">
{filteredData.map((item, index) => (
<Paper key={item.id} p="md" withBorder radius="md">
<Stack gap={4}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
No
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{index + 1}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Nama Program Kreatif Desa
</Text>
<Text fz="sm" fw={500} lh={1.4} lineClamp={2}>
{item.name}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Deskripsi Singkat
</Text>
<Text fz="sm" fw={500} c="dimmed" lh={1.4} lineClamp={2}>
{item.slug}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Ikon
</Text>
<Box mt={4} ta="center">
{iconMap[item.icon] && (
<Box title={item.icon}>
{React.createElement(iconMap[item.icon], { size: 24 })}
</Box>
)}
</Box>
</Box>
<Box ta="center" mt="sm">
<Button
size="xs"
radius="md"
variant="light"
color="blue"
onClick={() =>
router.push(`/admin/inovasi/program-kreatif-desa/${item.id}`)
}
>
<IconDeviceImac size={18} />
<Text ml={6} fz="sm" fw={500}>
Detail
</Text>
</Button>
</Box>
</Stack>
</Paper>
))}
</Stack>
</Box>
</Paper> </Paper>
<Center> <Center>
<Pagination <Pagination
value={page} value={page}
@@ -238,8 +341,8 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
window.scrollTo({ top: 0, behavior: 'smooth' }); window.scrollTo({ top: 0, behavior: 'smooth' });
}} }}
total={totalPages} total={totalPages}
mt="md" mt="xl"
mb="md" mb="xl"
color="blue" color="blue"
radius="md" radius="md"
/> />
@@ -248,4 +351,4 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
); );
} }
export default ProgramKreatifDesa; export default ProgramKreatifDesa;

View File

@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core'; import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import { IconRecycle, IconTrash } from '@tabler/icons-react'; import { IconRecycle, IconTrash } from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation'; import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
@@ -58,35 +58,76 @@ function LayoutTabsPengelolaanSampahBankSampah({ children }: { children: React.R
keepMounted={false} keepMounted={false}
> >
{/* ✅ Scroll horizontal wrapper */} {/* ✅ Scroll horizontal wrapper */}
<ScrollArea type="auto" offsetScrollbars> <Box visibleFrom='md' pb={10}>
<TabsList <ScrollArea type="auto" offsetScrollbars>
p="sm" <TabsList
style={{ p="sm"
background: "linear-gradient(135deg, #e7ebf7, #f9faff)", style={{
borderRadius: "1rem", background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)", borderRadius: "1rem",
display: "flex", boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
flexWrap: "nowrap", display: "flex",
gap: "0.5rem", flexWrap: "nowrap",
paddingInline: "0.5rem", gap: "0.5rem",
}} paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
> }}
{tabs.map((tab, i) => ( >
<TabsTab {tabs.map((tab, i) => (
key={i} <TabsTab
value={tab.value} key={i}
leftSection={tab.icon} value={tab.value}
style={{ leftSection={tab.icon}
fontWeight: 600, style={{
fontSize: "0.9rem", fontWeight: 600,
transition: "all 0.2s ease", fontSize: "0.9rem",
}} transition: "all 0.2s ease",
> flexShrink: 0, // ✅ jangan mengecil aneh-aneh
{tab.label} }}
</TabsTab> >
))} {tab.label}
</TabsList> </TabsTab>
</ScrollArea> ))}
</TabsList>
</ScrollArea>
</Box>
<Box hiddenFrom='md' pb={10}>
<ScrollArea
type="auto"
offsetScrollbars={false}
w="100%"
>
<TabsList
p="xs" // lebih kecil
style={{
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
borderRadius: "1rem",
display: "flex",
flexWrap: "nowrap",
gap: "0.5rem",
width: "max-content", // ⬅️ kunci
maxWidth: "100%", // ⬅️ penting
}}
>
{tabs.map((tab, i) => (
<TabsTab
key={i}
value={tab.value}
leftSection={tab.icon}
style={{
fontWeight: 600,
fontSize: "0.9rem",
paddingInline: "0.75rem", // ⬅️ lebih ramping
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
}}
>
{tab.label}
</TabsTab>
))}
</TabsList>
</ScrollArea>
</Box>
{tabs.map((tab, i) => ( {tabs.map((tab, i) => (
<TabsPanel <TabsPanel

View File

@@ -1,6 +1,28 @@
'use client'
import { usePathname } from 'next/navigation';
import LayoutTabsPengelolaanSampahBankSampah from './_lib/layoutTabs'; import LayoutTabsPengelolaanSampahBankSampah from './_lib/layoutTabs';
import { Box } from '@mantine/core';
function PengelolaanSampahBankSampah({ children }: { children: React.ReactNode }) { function PengelolaanSampahBankSampah({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
// Contoh path:
// - /darmasaba/desa/berita/semua → panjang 5 → list
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
const segments = pathname.split('/').filter(Boolean);
const isDetailPage = segments.length >= 5;
if (isDetailPage) {
// Tampilkan tanpa tab menu
return (
<Box>
{children}
</Box>
);
}
return ( return (
<LayoutTabsPengelolaanSampahBankSampah> <LayoutTabsPengelolaanSampahBankSampah>
{children} {children}