Fix QC Kak Inno 22 Des
Fix QC Kak Ayu 22 Des Fix Tampilan Admin Menu Inovasi
This commit is contained in:
@@ -194,7 +194,7 @@ function ListProgramKemiskinan({ search }: { search: string }) {
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item) => (
|
||||
<Paper key={item.id} withBorder p="md" radius="sm">
|
||||
<Stack gap={4}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz="sm" fw={600} lh={1.4}>
|
||||
Judul Program
|
||||
|
||||
@@ -40,7 +40,7 @@ function DetailAjukanIdeInofativDesa() {
|
||||
const data = state.findUnique.data;
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Tombol Kembali */}
|
||||
<Button
|
||||
variant="subtle"
|
||||
@@ -54,7 +54,7 @@ function DetailAjukanIdeInofativDesa() {
|
||||
{/* Card Utama */}
|
||||
<Paper
|
||||
withBorder
|
||||
w={{ base: "100%", md: "80%", lg: "60%" }}
|
||||
w={{ base: "100%", md: "70%" }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
Text,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
@@ -44,6 +44,7 @@ function AjukanIdeInovatif() {
|
||||
function ListAjukanIdeInovatif({ search }: { search: string }) {
|
||||
const state = useProxy(ajukanIdeInovatifState)
|
||||
const router = useRouter()
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
|
||||
|
||||
const {
|
||||
data,
|
||||
@@ -54,8 +55,8 @@ function ListAjukanIdeInovatif({ search }: { search: string }) {
|
||||
} = state.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search)
|
||||
}, [page, search])
|
||||
load(page, 10, debouncedSearch)
|
||||
}, [page, debouncedSearch])
|
||||
|
||||
const filteredData = data || []
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ function EditDigitalSmartVillage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
|
||||
@@ -49,7 +49,7 @@ function DetailDesaDigital() {
|
||||
const data = stateDesaDigital.findUnique.data;
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Tombol Kembali */}
|
||||
<Button
|
||||
variant="subtle"
|
||||
@@ -63,7 +63,7 @@ function DetailDesaDigital() {
|
||||
{/* Card Utama */}
|
||||
<Paper
|
||||
withBorder
|
||||
w={{ base: "100%", md: "60%" }}
|
||||
w={{ base: "100%", md: "70%" }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
@@ -77,12 +77,12 @@ function DetailDesaDigital() {
|
||||
{/* Sub Card Detail */}
|
||||
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
|
||||
<Stack gap="sm">
|
||||
<Box>
|
||||
<Box pl={5}>
|
||||
<Text fz="lg" fw="bold">Judul</Text>
|
||||
<Text fz="md" c="dimmed">{data?.name || '-'}</Text>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Box pl={5}>
|
||||
<Text fz="lg" fw="bold">Deskripsi</Text>
|
||||
<Text
|
||||
fz="md"
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function CreateDesaDigital() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Header dengan tombol kembali */}
|
||||
<Group mb="md" align="center">
|
||||
<Button
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
Text,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
@@ -45,28 +45,31 @@ function DesaDigitalSmartVillage() {
|
||||
function ListDesaDigitalSmartVillage({ search }: { search: string }) {
|
||||
const state = useProxy(desaDigitalState);
|
||||
const router = useRouter();
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||
|
||||
const { data, page, totalPages, loading, load } = state.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py="lg">
|
||||
<Skeleton height={600} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box py="lg">
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="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
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color="blue"
|
||||
@@ -78,15 +81,34 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
|
||||
Tambah Baru
|
||||
</Button>
|
||||
</Group>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table highlightOnHover>
|
||||
|
||||
{/* Desktop Table */}
|
||||
<Box visibleFrom="sm">
|
||||
<Table
|
||||
highlightOnHover
|
||||
miw={0}
|
||||
style={{
|
||||
tableLayout: 'fixed',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '30%' }}>Nama Inovasi</TableTh>
|
||||
<TableTh style={{ width: '50%' }}>
|
||||
Deskripsi Singkat Inovasi
|
||||
<TableTh style={{ width: '30%' }}>
|
||||
<Text fz="sm" fw={600} lh={1.4}>
|
||||
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 style={{ width: '20%' }}>Aksi</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
@@ -94,21 +116,18 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={200}>
|
||||
<Text fw={500} truncate="end" lineClamp={1}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</Box>
|
||||
<Text fz="md" fw={500} lh={1.5} truncate="end">
|
||||
{item.name}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Box w={200}>
|
||||
<Text
|
||||
fz="sm"
|
||||
c="dimmed"
|
||||
lineClamp={1}
|
||||
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
||||
/>
|
||||
</Box>
|
||||
<Text
|
||||
fz="sm"
|
||||
c="dimmed"
|
||||
lh={1.5}
|
||||
lineClamp={1}
|
||||
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
||||
/>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
@@ -121,7 +140,9 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
|
||||
}
|
||||
>
|
||||
<IconDeviceImac size={20} />
|
||||
<Text ml={5}>Detail</Text>
|
||||
<Text ml={5} fz="sm" fw={500} lh={1.4}>
|
||||
Detail
|
||||
</Text>
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
@@ -129,8 +150,8 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
|
||||
) : (
|
||||
<TableTr>
|
||||
<TableTd colSpan={3}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">
|
||||
<Center py="xl">
|
||||
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||
Tidak ada data inovasi digital yang cocok
|
||||
</Text>
|
||||
</Center>
|
||||
@@ -140,6 +161,64 @@ function ListDesaDigitalSmartVillage({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</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>
|
||||
<Center>
|
||||
<Pagination
|
||||
|
||||
@@ -123,7 +123,7 @@ function EditInfoTeknologiTepatGuna() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Tombol back + title */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
|
||||
@@ -40,7 +40,7 @@ function DetailInfoTeknologiTepatGuna() {
|
||||
const data = stateInfoTekno.findUnique.data
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Tombol Kembali */}
|
||||
<Button
|
||||
variant="subtle"
|
||||
@@ -54,7 +54,7 @@ function DetailInfoTeknologiTepatGuna() {
|
||||
{/* Card Utama */}
|
||||
<Paper
|
||||
withBorder
|
||||
w={{ base: "100%", md: "50%" }}
|
||||
w={{ base: "100%", md: "70%" }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
|
||||
@@ -74,7 +74,7 @@ function CreateInfoTeknologiTepatGuna() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
|
||||
@@ -16,9 +16,9 @@ import {
|
||||
TableThead,
|
||||
TableTr,
|
||||
Text,
|
||||
Title
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
@@ -45,28 +45,31 @@ function InfoTeknologiTepatGuna() {
|
||||
function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
|
||||
const state = useProxy(infoTeknoState);
|
||||
const router = useRouter();
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||
|
||||
const { data, page, totalPages, loading, load } = state.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py={{ base: 'sm', md: 'lg' }}>
|
||||
<Skeleton height={600} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||
<Box py={{ base: 'sm', md: 'lg' }}>
|
||||
<Paper withBorder bg={colors['white-1']} p={{ base: 'sm', md: 'lg' }} shadow="md" radius="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
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color="blue"
|
||||
@@ -79,40 +82,57 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table highlightOnHover>
|
||||
{/* Desktop Table */}
|
||||
<Box visibleFrom="md">
|
||||
<Table
|
||||
highlightOnHover
|
||||
miw={0}
|
||||
style={{
|
||||
tableLayout: 'fixed',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '30%' }}>
|
||||
Nama Info Teknologi
|
||||
<Text fz="sm" fw={600} lh={1.4}>
|
||||
Nama Info Teknologi
|
||||
</Text>
|
||||
</TableTh>
|
||||
<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 style={{ width: '20%' }}>Aksi</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd style={{ width: '30%' }}>
|
||||
<Text fw={500} truncate="end" lineClamp={1}>
|
||||
<TableTd>
|
||||
<Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '50%' }}>
|
||||
<TableTd>
|
||||
<Text
|
||||
fz="sm"
|
||||
lh={1.5}
|
||||
c="dimmed"
|
||||
lineClamp={1}
|
||||
truncate
|
||||
fz="sm"
|
||||
c="dimmed"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item.deskripsi || '-',
|
||||
}}
|
||||
/>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '20%' }}>
|
||||
<TableTd>
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
@@ -134,7 +154,7 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
|
||||
<TableTr>
|
||||
<TableTd colSpan={3}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">
|
||||
<Text fz="sm" c="dimmed" ta="center">
|
||||
Tidak ada data Info Teknologi yang cocok
|
||||
</Text>
|
||||
</Center>
|
||||
@@ -144,6 +164,61 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</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>
|
||||
|
||||
<Center>
|
||||
@@ -164,4 +239,4 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default InfoTeknologiTepatGuna;
|
||||
export default InfoTeknologiTepatGuna;
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
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 { usePathname, useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
@@ -57,36 +57,76 @@ function LayoutTabsKolaborasi({ children }: { children: React.ReactNode }) {
|
||||
keepMounted={false}
|
||||
>
|
||||
{/* ✅ Scroll horizontal wrapper biar rapi */}
|
||||
<ScrollArea type="auto" offsetScrollbars>
|
||||
<TabsList
|
||||
p="sm"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||
borderRadius: "1rem",
|
||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "0.5rem",
|
||||
paddingInline: "0.5rem",
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsTab
|
||||
key={i}
|
||||
value={tab.value}
|
||||
leftSection={tab.icon}
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.9rem",
|
||||
transition: "all 0.2s ease",
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTab>
|
||||
))}
|
||||
</TabsList>
|
||||
</ScrollArea>
|
||||
<Box visibleFrom='md' pb={10}>
|
||||
<ScrollArea type="auto" offsetScrollbars>
|
||||
<TabsList
|
||||
p="sm"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||
borderRadius: "1rem",
|
||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "0.5rem",
|
||||
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsTab
|
||||
key={i}
|
||||
value={tab.value}
|
||||
leftSection={tab.icon}
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.9rem",
|
||||
transition: "all 0.2s ease",
|
||||
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTab>
|
||||
))}
|
||||
</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) => (
|
||||
<TabsPanel
|
||||
key={i}
|
||||
|
||||
@@ -1,7 +1,29 @@
|
||||
'use client'
|
||||
import React from 'react';
|
||||
import LayoutTabsKolaborasi from './_lib/layoutTabs';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { Box } from '@mantine/core';
|
||||
|
||||
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 (
|
||||
<LayoutTabsKolaborasi>
|
||||
{children}
|
||||
|
||||
@@ -118,7 +118,7 @@ function EditKolaborasiInovasi() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: "sm", md: "lg" }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors["blue-button"]} size={24} />
|
||||
|
||||
@@ -41,7 +41,7 @@ function DetailKolaborasiInovasi() {
|
||||
const data = kolaborasiState.findUnique.data;
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Tombol kembali */}
|
||||
<Button
|
||||
variant="subtle"
|
||||
|
||||
@@ -55,7 +55,7 @@ function CreateProgramKreatifDesa() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Back Button */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
Text,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -45,69 +46,107 @@ function KolaborasiInovasi() {
|
||||
function ListKolaborasiInovasi({ search }: { search: string }) {
|
||||
const listState = useProxy(kolaborasiInovasiState);
|
||||
const router = useRouter();
|
||||
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||
const { data, loading, page, totalPages, load } = listState.findMany;
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py={{ base: 'xs', md: 'md' }}>
|
||||
<Skeleton height={600} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Daftar Kolaborasi Inovasi</Title>
|
||||
<Box py={{ base: 'xs', md: 'md' }}>
|
||||
<Paper withBorder bg={colors['white-1']} p={{ base: 'sm', md: 'lg' }} shadow="md" radius="md">
|
||||
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||
<Title order={4} lh={1.2}>
|
||||
Daftar Kolaborasi Inovasi
|
||||
</Title>
|
||||
<Button
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color="blue"
|
||||
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
|
||||
</Button>
|
||||
</Group>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table highlightOnHover>
|
||||
|
||||
{/* Desktop Table */}
|
||||
<Box visibleFrom="md">
|
||||
<Table
|
||||
highlightOnHover
|
||||
miw={0}
|
||||
style={{
|
||||
tableLayout: 'fixed',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '5%' }}>No</TableTh>
|
||||
<TableTh style={{ width: '25%' }}>Nama Kolaborasi Inovasi</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>Tahun</TableTh>
|
||||
<TableTh style={{ width: '35%' }}>Deskripsi Singkat</TableTh>
|
||||
<TableTh style={{ width: '10%' }}>Aksi</TableTh>
|
||||
<TableTh style={{ width: '5%' }}>
|
||||
<Text fz="xs" fw={600} lh={1.4} ta="center">
|
||||
No
|
||||
</Text>
|
||||
</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>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item, index) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{index + 1}</TableTd>
|
||||
<TableTd>
|
||||
<Text fw={500} truncate="end" lineClamp={1}>
|
||||
{item.name}
|
||||
<TableTd ta="center">
|
||||
<Text fz="sm" fw={500} lh={1.45}>
|
||||
{index + 1}
|
||||
</Text>
|
||||
</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}
|
||||
</Text>
|
||||
</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}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<TableTd ta="center">
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
@@ -115,7 +154,9 @@ function ListKolaborasiInovasi({ search }: { search: string }) {
|
||||
color="blue"
|
||||
leftSection={<IconDeviceImac size={16} />}
|
||||
onClick={() =>
|
||||
router.push(`/admin/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/${item.id}`)
|
||||
router.push(
|
||||
`/admin/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/${item.id}`
|
||||
)
|
||||
}
|
||||
>
|
||||
Detail
|
||||
@@ -127,7 +168,9 @@ function ListKolaborasiInovasi({ search }: { search: string }) {
|
||||
<TableTr>
|
||||
<TableTd colSpan={5}>
|
||||
<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>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
@@ -135,23 +178,95 @@ function ListKolaborasiInovasi({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</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>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => {
|
||||
load(newPage, 10);
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
color="blue"
|
||||
radius="md"
|
||||
/>
|
||||
</Center>
|
||||
|
||||
{totalPages > 1 && (
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => {
|
||||
load(newPage, 10);
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}}
|
||||
total={totalPages}
|
||||
mt={{ base: 'sm', md: 'md' }}
|
||||
mb={{ base: 'sm', md: 'md' }}
|
||||
color="blue"
|
||||
radius="md"
|
||||
/>
|
||||
</Center>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default KolaborasiInovasi;
|
||||
export default KolaborasiInovasi;
|
||||
@@ -136,7 +136,7 @@ function EditMitraKolaborasi() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
|
||||
@@ -68,7 +68,7 @@ function CreateMitraKolaborasi() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Back Button + Title */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
TableThead,
|
||||
TableTr,
|
||||
Text,
|
||||
Title
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { IconEdit, IconPlus, IconSearch, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@@ -27,6 +27,7 @@ import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import mitraKolaborasi from '../../../_state/inovasi/mitra-kolaborasi';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
|
||||
function MitraKolaborasi() {
|
||||
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;
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py="lg">
|
||||
<Skeleton height={600} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box py="lg">
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Daftar Mitra Kolaborasi</Title>
|
||||
<Group justify="space-between" mb="lg">
|
||||
<Title
|
||||
order={4}
|
||||
lh={{ base: 1.2, md: 1.15 }}
|
||||
>
|
||||
Daftar Mitra Kolaborasi
|
||||
</Title>
|
||||
<Button
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color="blue"
|
||||
@@ -93,41 +101,82 @@ function ListMitraKolaborasi({ search }: { search: string }) {
|
||||
Tambah Baru
|
||||
</Button>
|
||||
</Group>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table highlightOnHover>
|
||||
|
||||
{/* Desktop Table */}
|
||||
<Box visibleFrom="md">
|
||||
<Table
|
||||
highlightOnHover
|
||||
miw={0}
|
||||
style={{
|
||||
tableLayout: 'fixed',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '10%' }}>No</TableTh>
|
||||
<TableTh style={{ width: '30%' }}>Nama Mitra</TableTh>
|
||||
<TableTh style={{ width: '25%' }}>Image</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>Delete</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>Edit</TableTh>
|
||||
<TableTh style={{ width: '10%' }}>
|
||||
<Text fz={{ base: 'sm', md: 'md' }} fw={600} lh={1.4}>
|
||||
No
|
||||
</Text>
|
||||
</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>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item, index) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{index + 1}</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}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Box
|
||||
w={70}
|
||||
h={70}
|
||||
>
|
||||
<Box w={70} h={70}>
|
||||
{item.image?.link ? (
|
||||
<Image
|
||||
loading="lazy"
|
||||
src={item.image.link}
|
||||
alt={item.name}
|
||||
fit="cover"
|
||||
radius="sm"
|
||||
/>
|
||||
) : (
|
||||
<Box bg={colors['blue-button']} w="100%" h="100%" />
|
||||
<Box
|
||||
bg={colors['blue-button']}
|
||||
w="100%"
|
||||
h="100%"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</TableTd>
|
||||
@@ -167,8 +216,8 @@ function ListMitraKolaborasi({ search }: { search: string }) {
|
||||
) : (
|
||||
<TableTr>
|
||||
<TableTd colSpan={5}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">
|
||||
<Center py="xl">
|
||||
<Text c="dimmed" fz="sm" ta="center">
|
||||
Tidak ada data mitra kolaborasi yang tersedia
|
||||
</Text>
|
||||
</Center>
|
||||
@@ -178,7 +227,96 @@ function ListMitraKolaborasi({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</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>
|
||||
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
@@ -187,14 +325,13 @@ function ListMitraKolaborasi({ search }: { search: string }) {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
mt="lg"
|
||||
mb="lg"
|
||||
color="blue"
|
||||
radius="md"
|
||||
/>
|
||||
</Center>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
@@ -205,4 +342,4 @@ function ListMitraKolaborasi({ search }: { search: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default MitraKolaborasi;
|
||||
export default MitraKolaborasi;
|
||||
@@ -2,6 +2,7 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
Box,
|
||||
ScrollArea,
|
||||
Stack,
|
||||
Tabs,
|
||||
@@ -84,36 +85,76 @@ function LayoutTabsLayananOnlineDesa({ children }: { children: React.ReactNode }
|
||||
keepMounted={false}
|
||||
>
|
||||
{/* ✅ Scroll horizontal biar gak overflow */}
|
||||
<ScrollArea type="auto" offsetScrollbars>
|
||||
<TabsList
|
||||
p="sm"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||
borderRadius: "1rem",
|
||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "0.5rem",
|
||||
paddingInline: "0.5rem",
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsTab
|
||||
key={i}
|
||||
value={tab.value}
|
||||
leftSection={tab.icon}
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.9rem",
|
||||
transition: "all 0.2s ease",
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTab>
|
||||
))}
|
||||
</TabsList>
|
||||
</ScrollArea>
|
||||
|
||||
<Box visibleFrom='md' pb={10}>
|
||||
<ScrollArea type="auto" offsetScrollbars>
|
||||
<TabsList
|
||||
p="sm"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||
borderRadius: "1rem",
|
||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "0.5rem",
|
||||
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsTab
|
||||
key={i}
|
||||
value={tab.value}
|
||||
leftSection={tab.icon}
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.9rem",
|
||||
transition: "all 0.2s ease",
|
||||
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTab>
|
||||
))}
|
||||
</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) => (
|
||||
<TabsPanel
|
||||
key={i}
|
||||
|
||||
@@ -42,8 +42,8 @@ function DetailAdministrasiOnline() {
|
||||
const data = stateAdminOnline.findUnique.data;
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Group justify='space-between' align='center' w={{ base: "100%", md: "50%" }} mb={10}>
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
<Group justify='space-between' align='center' w={{ base: "100%", md: "70%" }} mb={10}>
|
||||
{/* Tombol Kembali */}
|
||||
<Button
|
||||
variant="subtle"
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
Text,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
@@ -44,36 +44,55 @@ function AdministrasiOnline() {
|
||||
function ListAdministrasiOnline({ search }: { search: string }) {
|
||||
const state = useProxy(layananonlineDesa.administrasiOnline);
|
||||
const router = useRouter();
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||
|
||||
const { data, page, totalPages, loading, load } = state.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
|
||||
const filteredData = data || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py={{ base: 'sm', md: 'md' }}>
|
||||
<Skeleton height={600} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||
<Title order={4}>Daftar Administrasi Online</Title>
|
||||
<Box py={{ base: 'sm', md: 'md' }}>
|
||||
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
|
||||
<Title order={4} lh={1.2} mb={{ base: 'md', md: 'lg' }}>
|
||||
Daftar Administrasi Online
|
||||
</Title>
|
||||
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table highlightOnHover>
|
||||
{/* Desktop Table */}
|
||||
<Box visibleFrom="md">
|
||||
<Table
|
||||
highlightOnHover
|
||||
miw={0}
|
||||
style={{
|
||||
tableLayout: 'fixed',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '25%' }}>Nama</TableTh>
|
||||
<TableTh style={{ width: '25%' }}>Alamat</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Nomor Telepon</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
|
||||
<TableTh style={{ width: '25%' }}>
|
||||
<Text fz="sm" fw={600} lh={1.4}>Nama</Text>
|
||||
</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>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
@@ -81,17 +100,17 @@ function ListAdministrasiOnline({ search }: { search: string }) {
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Text fw={500} truncate="end" lineClamp={1}>
|
||||
<Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text fz="sm" c="dimmed" lineClamp={1}>
|
||||
<Text fz="sm" c="gray.7" lh={1.5} lineClamp={1}>
|
||||
{item.alamat}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text fz="sm" c="dimmed" lineClamp={1}>
|
||||
<Text fz="sm" c="gray.7" lh={1.5} lineClamp={1}>
|
||||
{item.nomorTelepon || '-'}
|
||||
</Text>
|
||||
</TableTd>
|
||||
@@ -116,8 +135,10 @@ function ListAdministrasiOnline({ search }: { search: string }) {
|
||||
) : (
|
||||
<TableTr>
|
||||
<TableTd colSpan={4}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">Tidak ada data administrasi online yang cocok</Text>
|
||||
<Center py={24}>
|
||||
<Text c="gray.6" fz="sm" lh={1.4}>
|
||||
Tidak ada data administrasi online yang cocok
|
||||
</Text>
|
||||
</Center>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
@@ -125,6 +146,55 @@ function ListAdministrasiOnline({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</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>
|
||||
|
||||
<Center>
|
||||
|
||||
@@ -90,7 +90,7 @@ function EditJenisLayanan() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Button
|
||||
|
||||
@@ -40,7 +40,7 @@ function DetailJenisLayanan() {
|
||||
const data = state.findUnique.data
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Tombol kembali */}
|
||||
<Button
|
||||
variant="subtle"
|
||||
@@ -54,7 +54,7 @@ function DetailJenisLayanan() {
|
||||
{/* Card utama */}
|
||||
<Paper
|
||||
withBorder
|
||||
w={{ base: "100%", md: "60%" }}
|
||||
w={{ base: "100%", md: "70%" }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
|
||||
@@ -50,7 +50,7 @@ function CreateJenisLayanan() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Header dengan tombol back */}
|
||||
<Group mb="md">
|
||||
<Button
|
||||
|
||||
@@ -16,9 +16,10 @@ import {
|
||||
TableThead,
|
||||
TableTr,
|
||||
Text,
|
||||
Title
|
||||
Title,
|
||||
useMantineTheme
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
@@ -45,28 +46,35 @@ function JenisLayanan() {
|
||||
function ListJenisLayanan({ search }: { search: string }) {
|
||||
const stateList = useProxy(layananonlineDesa.jenisLayanan);
|
||||
const router = useRouter();
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||
|
||||
const { data, page, totalPages, loading, load } = stateList.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
|
||||
const filteredData = data || [];
|
||||
const theme = useMantineTheme();
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py="lg">
|
||||
<Skeleton height={600} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Stack py="lg" gap="xl">
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="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
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color="blue"
|
||||
@@ -80,13 +88,34 @@ function ListJenisLayanan({ search }: { search: string }) {
|
||||
Tambah Baru
|
||||
</Button>
|
||||
</Group>
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table highlightOnHover>
|
||||
|
||||
{/* Desktop Table */}
|
||||
<Box visibleFrom="md">
|
||||
<Table
|
||||
highlightOnHover
|
||||
miw={0}
|
||||
style={{
|
||||
tableLayout: 'fixed',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '30%' }}>Nama Jenis Layanan</TableTh>
|
||||
<TableTh style={{ width: '40%' }}>Deskripsi</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
|
||||
<TableTh style={{ width: '30%' }}>
|
||||
<Text fz="sm" fw={600} lh={1.4} c={theme.black}>
|
||||
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>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
@@ -94,16 +123,29 @@ function ListJenisLayanan({ search }: { search: string }) {
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<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}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '40%' }}>
|
||||
<Text fz="sm" c="dimmed" lineClamp={2}>
|
||||
<TableTd style={{ width: '55%' }}>
|
||||
<Text
|
||||
fz="sm"
|
||||
fw={500}
|
||||
lh={1.5}
|
||||
c={theme.black}
|
||||
lineClamp={2}
|
||||
>
|
||||
{item.deskripsi || '-'}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '15%' }}>
|
||||
<TableTd style={{ width: '15%' }} ta="center">
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
@@ -124,8 +166,8 @@ function ListJenisLayanan({ search }: { search: string }) {
|
||||
) : (
|
||||
<TableTr>
|
||||
<TableTd colSpan={3}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">
|
||||
<Center py="xl">
|
||||
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||
Tidak ada jenis layanan yang cocok
|
||||
</Text>
|
||||
</Center>
|
||||
@@ -135,22 +177,76 @@ function ListJenisLayanan({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</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>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => {
|
||||
load(newPage, 10);
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
color="blue"
|
||||
radius="md"
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
|
||||
{totalPages > 1 && (
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => {
|
||||
load(newPage, 10);
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
color="blue"
|
||||
radius="md"
|
||||
/>
|
||||
</Center>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ function EditJenisPengaduan() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Button
|
||||
|
||||
@@ -47,7 +47,7 @@ function CreateJenisPengaduan() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
Text,
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
@@ -48,12 +48,13 @@ function ListJenisPengaduan({ search }: { search: string }) {
|
||||
const router = useRouter();
|
||||
const [modalHapus, setModalHapus] = useState(false);
|
||||
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(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
|
||||
const handleHapus = async () => {
|
||||
if (selectedId) {
|
||||
@@ -63,21 +64,23 @@ function ListJenisPengaduan({ search }: { search: string }) {
|
||||
}
|
||||
};
|
||||
|
||||
const filteredData = data || []
|
||||
const filteredData = data || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py={{ base: 'md', md: 'lg' }}>
|
||||
<Skeleton height={500} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Daftar Jenis Pengaduan</Title>
|
||||
<Box py={{ base: 'md', md: 'lg' }}>
|
||||
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
|
||||
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||
<Title order={4} lh={1.2}>
|
||||
Daftar Jenis Pengaduan
|
||||
</Title>
|
||||
<Button
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color="blue"
|
||||
@@ -92,13 +95,33 @@ function ListJenisPengaduan({ search }: { search: string }) {
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
<Box style={{ overflowX: 'auto' }}>
|
||||
<Table highlightOnHover>
|
||||
{/* Desktop Table */}
|
||||
<Box visibleFrom="md">
|
||||
<Table
|
||||
highlightOnHover
|
||||
miw={0}
|
||||
style={{
|
||||
tableLayout: 'fixed',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '60%' }}>Nama Jenis Pengaduan</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Edit</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Hapus</TableTh>
|
||||
<TableTh style={{ width: '60%' }}>
|
||||
<Text fz="sm" fw={600} lh={1.4} c="dimmed">
|
||||
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>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
@@ -106,7 +129,7 @@ function ListJenisPengaduan({ search }: { search: string }) {
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Text fw={500} truncate="end" lineClamp={1}>
|
||||
<Text fz="md" fw={500} lh={1.5} truncate="end">
|
||||
{item.nama}
|
||||
</Text>
|
||||
</TableTd>
|
||||
@@ -147,7 +170,7 @@ function ListJenisPengaduan({ search }: { search: string }) {
|
||||
<TableTr>
|
||||
<TableTd colSpan={3}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">
|
||||
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||
Tidak ada jenis pengaduan yang cocok
|
||||
</Text>
|
||||
</Center>
|
||||
@@ -157,6 +180,63 @@ function ListJenisPengaduan({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</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>
|
||||
|
||||
<Center>
|
||||
@@ -167,14 +247,13 @@ function ListJenisPengaduan({ search }: { search: string }) {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
mt={{ base: 'sm', md: 'md' }}
|
||||
mb={{ base: 'sm', md: 'md' }}
|
||||
color="blue"
|
||||
radius="md"
|
||||
/>
|
||||
</Center>
|
||||
|
||||
{/* Modal Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
@@ -185,4 +264,4 @@ function ListJenisPengaduan({ search }: { search: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default JenisPengaduan;
|
||||
export default JenisPengaduan;
|
||||
@@ -1,7 +1,28 @@
|
||||
'use client'
|
||||
import { Box } from "@mantine/core";
|
||||
import LayoutTabsLayananOnlineDesa from "./_lib/layoutTabs";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
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 (
|
||||
<LayoutTabsLayananOnlineDesa>
|
||||
{children}
|
||||
|
||||
@@ -41,7 +41,7 @@ function DetailPengaduanMasyarakat() {
|
||||
const data = pengaduanState.pengaduanMasyarakat.findUnique.data;
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Tombol Kembali */}
|
||||
<Button
|
||||
variant="subtle"
|
||||
@@ -55,7 +55,7 @@ function DetailPengaduanMasyarakat() {
|
||||
{/* Card Detail */}
|
||||
<Paper
|
||||
withBorder
|
||||
w={{ base: "100%", md: "60%" }}
|
||||
w={{ base: "100%", md: "70%" }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
|
||||
@@ -19,8 +19,7 @@ import {
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
@@ -46,6 +45,7 @@ function PengaduanMasyarakat() {
|
||||
function ListPengaduanMasyarakat({ search }: { search: string }) {
|
||||
const listState = useProxy(layananonlineDesa.pengaduanMasyarakat);
|
||||
const router = useRouter();
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
@@ -55,56 +55,84 @@ function ListPengaduanMasyarakat({ search }: { search: string }) {
|
||||
} = listState.findMany;
|
||||
|
||||
useShallowEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
|
||||
const filteredData = data || []
|
||||
const filteredData = data || [];
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py={{ base: 'sm', md: 'md' }}>
|
||||
<Skeleton height={600} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Daftar Pengaduan Masyarakat</Title>
|
||||
<Box py={{ base: 'sm', md: 'md' }}>
|
||||
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
|
||||
{/* Section Header */}
|
||||
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||
<Title order={4} lh={1.2}>
|
||||
Daftar Pengaduan Masyarakat
|
||||
</Title>
|
||||
</Group>
|
||||
<Box style={{ overflowX: "auto" }}>
|
||||
<Table highlightOnHover>
|
||||
|
||||
{/* Desktop Table */}
|
||||
<Box visibleFrom="md">
|
||||
<Table
|
||||
highlightOnHover
|
||||
miw={0}
|
||||
style={{
|
||||
tableLayout: 'fixed',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '25%' }}>Nama</TableTh>
|
||||
<TableTh style={{ width: '25%' }}>Email</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Nomor Telepon</TableTh>
|
||||
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
|
||||
<TableTh style={{ width: '25%' }}>
|
||||
<Text fz="sm" fw={600} lh={1.4}>Nama</Text>
|
||||
</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>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.length > 0 ? (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd style={{ width: '25%' }}>
|
||||
<Text fw={500} truncate="end" lineClamp={1}>{item.name}</Text>
|
||||
<TableTd>
|
||||
<Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '25%' }}>
|
||||
<Text fz="sm" c="dimmed" truncate lineClamp={1}>{item.email}</Text>
|
||||
<TableTd>
|
||||
<Text fz="sm" c="dimmed" lh={1.5} truncate lineClamp={1}>
|
||||
{item.email}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '20%' }}>
|
||||
<Text fz="sm" c="dimmed" truncate lineClamp={1}>{item.nomorTelepon}</Text>
|
||||
<TableTd>
|
||||
<Text fz="sm" c="dimmed" lh={1.5} truncate lineClamp={1}>
|
||||
{item.nomorTelepon}
|
||||
</Text>
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '15%' }}>
|
||||
<TableTd>
|
||||
<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}`)}
|
||||
onClick={() =>
|
||||
router.push(`/admin/inovasi/layanan-online-desa/pengaduan-masyarakat/${item.id}`)
|
||||
}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
@@ -114,8 +142,10 @@ function ListPengaduanMasyarakat({ search }: { search: string }) {
|
||||
) : (
|
||||
<TableTr>
|
||||
<TableTd colSpan={4}>
|
||||
<Center py={20}>
|
||||
<Text color="dimmed">Tidak ada data pengaduan yang cocok</Text>
|
||||
<Center py={{ base: 'sm', md: 'lg' }}>
|
||||
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||
Tidak ada data pengaduan yang cocok
|
||||
</Text>
|
||||
</Center>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
@@ -123,7 +153,56 @@ function ListPengaduanMasyarakat({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</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>
|
||||
|
||||
{/* Pagination */}
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
@@ -142,4 +221,4 @@ function ListPengaduanMasyarakat({ search }: { search: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default PengaduanMasyarakat;
|
||||
export default PengaduanMasyarakat;
|
||||
@@ -160,7 +160,7 @@ function EditProgramKreatifDesa() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
<Group mb="md">
|
||||
<Button
|
||||
variant="subtle"
|
||||
|
||||
@@ -41,7 +41,7 @@ function DetailProgramKreatifDesa() {
|
||||
const data = stateProgramKreatif.findUnique.data
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
<Button
|
||||
variant="subtle"
|
||||
onClick={() => router.back()}
|
||||
@@ -53,7 +53,7 @@ function DetailProgramKreatifDesa() {
|
||||
|
||||
<Paper
|
||||
withBorder
|
||||
w={{ base: "100%", md: "50%" }}
|
||||
w={{ base: "100%", md: "70%" }}
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
|
||||
@@ -52,7 +52,7 @@ function CreateProgramKreatifDesa() {
|
||||
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||
{/* Tombol kembali */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
TableThead,
|
||||
TableTr,
|
||||
Text,
|
||||
Title
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import {
|
||||
IconCash,
|
||||
@@ -50,9 +50,10 @@ import React, { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import programKreatifState from '../../_state/inovasi/program-kreatif';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
|
||||
function ProgramKreatifDesa() {
|
||||
const [search, setSearch] = useState("");
|
||||
const [search, setSearch] = useState('');
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
@@ -71,12 +72,13 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
|
||||
const listState = useProxy(programKreatifState);
|
||||
const { data, loading, page, totalPages, load } = listState.findMany;
|
||||
const router = useRouter();
|
||||
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||
|
||||
useEffect(() => {
|
||||
load(page, 10, search);
|
||||
}, [page, search]);
|
||||
load(page, 10, debouncedSearch);
|
||||
}, [page, debouncedSearch]);
|
||||
|
||||
const filteredData = data || []
|
||||
const filteredData = data || [];
|
||||
|
||||
const iconMap: Record<string, React.FC<any>> = {
|
||||
ekowisata: IconLeaf,
|
||||
@@ -103,7 +105,7 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Stack py={{ base: 'sm', md: 'xl' }}>
|
||||
<Skeleton height={650} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
@@ -111,35 +113,44 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
|
||||
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper p="md" radius="md" shadow="sm" withBorder>
|
||||
<Stack>
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Daftar Program Kreatif Desa</Title>
|
||||
<Box py={{ base: 'sm', md: 'xl' }}>
|
||||
<Paper p="lg" radius="md" shadow="sm" withBorder>
|
||||
<Stack gap="xl">
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Title order={4} lh={1.15}>
|
||||
Daftar Program Kreatif Desa
|
||||
</Title>
|
||||
<Button
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color="blue"
|
||||
variant="light"
|
||||
onClick={() =>
|
||||
router.push(
|
||||
'/admin/inovasi/program-kreatif-desa/create'
|
||||
)
|
||||
router.push('/admin/inovasi/program-kreatif-desa/create')
|
||||
}
|
||||
>
|
||||
Tambah Baru
|
||||
</Button>
|
||||
</Group>
|
||||
<Table highlightOnHover striped>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>No</TableTh>
|
||||
<TableTh>Nama Program Kreatif Desa</TableTh>
|
||||
<TableTh>Deskripsi Singkat</TableTh>
|
||||
<TableTh>Ikon</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
</Table>
|
||||
|
||||
<Box visibleFrom="md">
|
||||
<Table
|
||||
highlightOnHover
|
||||
miw={0}
|
||||
striped
|
||||
style={{ tableLayout: 'fixed', width: '100%' }}
|
||||
>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<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">
|
||||
Tidak ada data program kreatif desa yang tersedia
|
||||
</Text>
|
||||
@@ -150,7 +161,7 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Box py={{ base: 'sm', md: 'xl' }}>
|
||||
<Paper
|
||||
withBorder
|
||||
bg={colors['white-1']}
|
||||
@@ -159,58 +170,83 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
|
||||
radius="md"
|
||||
h={{ base: 'auto', md: 650 }}
|
||||
>
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Daftar Program Kreatif Desa</Title>
|
||||
<Group justify="space-between" mb="xl" wrap="nowrap">
|
||||
<Title order={4} lh={1.15}>
|
||||
Daftar Program Kreatif Desa
|
||||
</Title>
|
||||
<Button
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color="blue"
|
||||
variant="light"
|
||||
onClick={() =>
|
||||
router.push(
|
||||
'/admin/inovasi/program-kreatif-desa/create'
|
||||
)
|
||||
router.push('/admin/inovasi/program-kreatif-desa/create')
|
||||
}
|
||||
>
|
||||
Tambah Baru
|
||||
</Button>
|
||||
</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>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '5%', textAlign: 'center' }}>No</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>
|
||||
<Text lineClamp={1} fw={"bold"} fz="sm">Nama Program Kreatif Desa</Text>
|
||||
<TableTh style={{ width: '5%', textAlign: 'center' }}>
|
||||
<Text fz="xs" fw={600} lh={1.2}>
|
||||
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 style={{ width: '35%' }}>Deskripsi Singkat</TableTh>
|
||||
<TableTh style={{ width: '10%', textAlign: 'center' }}>Ikon</TableTh>
|
||||
<TableTh style={{ width: '15%', textAlign: 'center' }}>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item, index) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd style={{ width: '5%', textAlign: 'center' }}>{index + 1}</TableTd>
|
||||
<TableTd style={{ width: '20%', wordWrap: 'break-word' }}>
|
||||
<Box w={200}>
|
||||
<Text fw={500} lineClamp={1}>{item.name}</Text>
|
||||
</Box>
|
||||
<TableTd ta="center" fz="sm" fw={500} lh={1.45}>
|
||||
{index + 1}
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '35%', wordWrap: 'break-word' }}>
|
||||
<Box w={150}>
|
||||
<Text fz="sm" c="dimmed" lineClamp={1}>
|
||||
{item.slug}
|
||||
</Text>
|
||||
</Box>
|
||||
<TableTd>
|
||||
<Text fz="sm" fw={500} lh={1.45} lineClamp={1}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</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] && (
|
||||
<Box title={item.icon}>
|
||||
{React.createElement(iconMap[item.icon], { size: 24 })}
|
||||
</Box>
|
||||
)}
|
||||
</TableTd>
|
||||
<TableTd style={{ width: '15%', textAlign: 'center' }}>
|
||||
<TableTd ta="center">
|
||||
<Button
|
||||
size="xs"
|
||||
radius="md"
|
||||
@@ -221,7 +257,9 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
|
||||
}
|
||||
>
|
||||
<IconDeviceImac size={18} />
|
||||
<Text ml={6}>Detail</Text>
|
||||
<Text ml={6} fz="sm" fw={500}>
|
||||
Detail
|
||||
</Text>
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
@@ -229,7 +267,72 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</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>
|
||||
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
@@ -238,8 +341,8 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
mt="xl"
|
||||
mb="xl"
|
||||
color="blue"
|
||||
radius="md"
|
||||
/>
|
||||
@@ -248,4 +351,4 @@ function ListProgramKreatifDesa({ search }: { search: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default ProgramKreatifDesa;
|
||||
export default ProgramKreatifDesa;
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
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 { usePathname, useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
@@ -58,35 +58,76 @@ function LayoutTabsPengelolaanSampahBankSampah({ children }: { children: React.R
|
||||
keepMounted={false}
|
||||
>
|
||||
{/* ✅ Scroll horizontal wrapper */}
|
||||
<ScrollArea type="auto" offsetScrollbars>
|
||||
<TabsList
|
||||
p="sm"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||
borderRadius: "1rem",
|
||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "0.5rem",
|
||||
paddingInline: "0.5rem",
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsTab
|
||||
key={i}
|
||||
value={tab.value}
|
||||
leftSection={tab.icon}
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.9rem",
|
||||
transition: "all 0.2s ease",
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTab>
|
||||
))}
|
||||
</TabsList>
|
||||
</ScrollArea>
|
||||
<Box visibleFrom='md' pb={10}>
|
||||
<ScrollArea type="auto" offsetScrollbars>
|
||||
<TabsList
|
||||
p="sm"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||
borderRadius: "1rem",
|
||||
boxShadow: "inset 0 0 10px rgba(0,0,0,0.05)",
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: "0.5rem",
|
||||
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab, i) => (
|
||||
<TabsTab
|
||||
key={i}
|
||||
value={tab.value}
|
||||
leftSection={tab.icon}
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "0.9rem",
|
||||
transition: "all 0.2s ease",
|
||||
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTab>
|
||||
))}
|
||||
</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) => (
|
||||
<TabsPanel
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
'use client'
|
||||
import { usePathname } from 'next/navigation';
|
||||
import LayoutTabsPengelolaanSampahBankSampah from './_lib/layoutTabs';
|
||||
import { Box } from '@mantine/core';
|
||||
|
||||
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 (
|
||||
<LayoutTabsPengelolaanSampahBankSampah>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user