Fix QC Kak Inno 16 Des

Fix QC Kak Ayu 16 Des
FIx UI Admin Mobile Menu PPID
Fix Search Admin Menu Landing Page & Menu PPID
This commit is contained in:
2025-12-17 17:37:58 +08:00
parent c8484357cb
commit dc8793e3ae
44 changed files with 2028 additions and 833 deletions

View File

@@ -1,7 +1,7 @@
'use client'
import { Box, Button, Group, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
@@ -106,7 +106,7 @@ function DetailSDGSDesa() {
size="md"
disabled={sdgsState.delete.loading}
>
<IconX size={20} />
<IconTrash size={20} />
</Button>
<Button

View File

@@ -1,7 +1,7 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -28,6 +28,8 @@ function SdgsDesa() {
function ListSdgsDesa({ search }: { search: string }) {
const listState = useProxy(sdgsDesa);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const {
data,
@@ -38,8 +40,8 @@ function ListSdgsDesa({ search }: { search: string }) {
} = listState.findMany;
useShallowEffect(() => {
load(page, 10, search);
}, [page, search]);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
const filteredData = data || [];
@@ -63,7 +65,7 @@ function ListSdgsDesa({ search }: { search: string }) {
</Title>
<Button
leftSection={<IconPlus size={18} />}
color={colors['blue-button']}
color='blue'
variant="light"
onClick={() => router.push('/admin/landing-page/SDGs/create')}
>
@@ -147,12 +149,18 @@ function ListSdgsDesa({ search }: { search: string }) {
{filteredData.map((item) => (
<Paper key={item.id} withBorder p="md" radius="md">
<Stack gap={4}>
<Text fz="sm" fw={600} lh={1.4}>
{item.name}
</Text>
<Text fz="xs" c="dark.6" lh={1.4}>
Jumlah: {item.jumlah || '0'}
</Text>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Nama SDGs Desa</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.name}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Jumlah</Text>
<Text fz="xs" c="dark.6" lh={1.4}>
{item.jumlah || '0'}
</Text>
</Box>
<Group justify="flex-end" mt="xs">
<Button
size="xs"

View File

@@ -368,6 +368,13 @@ function EditAPBDes() {
{ value: '2', label: 'Level 2 (Sub-kelompok)' },
{ value: '3', label: 'Level 3 (Detail)' },
]}
styles={{
option: {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
}}
value={String(newItem.level)}
onChange={(val) => setNewItem({ ...newItem, level: Number(val) || 1 })}
/>
@@ -378,6 +385,13 @@ function EditAPBDes() {
{ value: 'belanja', label: 'Belanja' },
{ value: 'pembiayaan', label: 'Pembiayaan' },
]}
styles={{
option: {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
}}
value={newItem.tipe}
onChange={(val) => setNewItem({ ...newItem, tipe: (val as any) || 'pendapatan' })}
/>

View File

@@ -353,6 +353,13 @@ function CreateAPBDes() {
{ value: '2', label: 'Level 2 (Sub-kelompok)' },
{ value: '3', label: 'Level 3 (Detail)' },
]}
styles={{
option: {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
}}
value={String(newItem.level)}
onChange={(val) => setNewItem({ ...newItem, level: Number(val) || 1 })}
/>
@@ -363,6 +370,13 @@ function CreateAPBDes() {
{ value: 'belanja', label: 'Belanja' },
{ value: 'pembiayaan', label: 'Pembiayaan' },
]}
styles={{
option: {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
}}
value={newItem.level === 1 ? null : newItem.tipe}
onChange={(val) => setNewItem({ ...newItem, tipe: val as any })}
disabled={newItem.level === 1}

View File

@@ -18,7 +18,7 @@ import {
Text,
Title,
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconFile, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -45,12 +45,13 @@ function APBDes() {
function ListAPBDes({ search }: { search: string }) {
const listState = useProxy(apbdes);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load } = listState.findMany;
useShallowEffect(() => {
load(page, 10, search);
}, [page, search]);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
const filteredData = data || [];
@@ -197,17 +198,17 @@ function ListAPBDes({ search }: { search: string }) {
<Text fz="sm" fw={600} lh={1.4}>
APBDes {item.tahun}
</Text>
<Group justify="space-between" wrap="nowrap">
<Text fz="sm" c="dimmed" lh={1.4}>
<Box>
<Text fz="sm"fw={600} lh={1.4}>
Tahun
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.tahun || '-'}
</Text>
</Group>
</Box>
<Group justify="space-between" wrap="nowrap">
<Text fz="sm" c="dimmed" lh={1.4}>
<Box>
<Text fz="sm"fw={600} lh={1.4}>
Dokumen
</Text>
{item.file?.link ? (
@@ -230,7 +231,7 @@ function ListAPBDes({ search }: { search: string }) {
Tidak ada
</Text>
)}
</Group>
</Box>
<Button
size="xs"

View File

@@ -151,7 +151,7 @@ function ListKategoriKegiatan({ search }: { search: string }) {
{item.name}
</Text>
</TableTd>
<TableTd w={60}>
<TableTd w={120}>
<Button
variant="light"
color="green"
@@ -161,7 +161,7 @@ function ListKategoriKegiatan({ search }: { search: string }) {
<IconEdit size={18} />
</Button>
</TableTd>
<TableTd w={60}>
<TableTd w={120}>
<Button
variant="light"
color="red"
@@ -191,7 +191,7 @@ function ListKategoriKegiatan({ search }: { search: string }) {
);
return (
<Box py={{ base: 'xl', md: 'xl' }}>
<Box py={{ base: 20, md: 20 }}>
<Paper bg={colors['white-1']} p={{ base: 'md', md: 'xl' }} shadow="md" radius="md">
<Group justify="space-between" mb={{ base: 'md', md: 'lg' }}>
<Title order={2} lh={1.2}>

View File

@@ -150,12 +150,18 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
filteredData.map((item) => (
<Paper key={item.id} p="sm" radius="md" withBorder shadow="xs">
<Stack gap="xs">
<Text fw={500} fz="sm" lh={1.5} lineClamp={1}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Nama Program</Text>
<Text fw={500} fz="sm" lh={1.5} lineClamp={1}>
{item.name || '-'}
</Text>
<Text fz="xs" c="dimmed" lh={1.5} lineClamp={1}>
Kategori: {item.kategori?.name || '-'}
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Kategori</Text>
<Text fz="xs" c="dimmed" lh={1.5} lineClamp={1}>
{item.kategori?.name || '-'}
</Text>
</Box>
<Group justify="flex-end">
<Button
size="xs"

View File

@@ -1,7 +1,7 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, 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';
@@ -32,6 +32,7 @@ function ListKategoriPrestasi({ search }: { search: string }) {
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const router = useRouter()
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
const handleHapus = () => {
if (selectedId) {
@@ -50,8 +51,8 @@ function ListKategoriPrestasi({ search }: { search: string }) {
} = stateKategori.findMany
useShallowEffect(() => {
load(page, 10, search)
}, [page, search])
load(page, 10, debouncedSearch)
}, [page, debouncedSearch])
const filteredData = data || []

View File

@@ -1,7 +1,7 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import { useDebouncedValue, useMediaQuery, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -29,6 +29,7 @@ function ListPrestasi({ search }: { search: string }) {
const listState = useProxy(prestasiState.prestasiDesa)
const router = useRouter();
const isMobile = useMediaQuery('(max-width: 768px)');
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
const {
data,
@@ -39,8 +40,8 @@ function ListPrestasi({ search }: { search: string }) {
} = listState.findMany
useShallowEffect(() => {
load(page, 10, search);
}, []);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
const filteredData = data || []
@@ -71,44 +72,46 @@ function ListPrestasi({ search }: { search: string }) {
</Group>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover miw={0}>
<Box visibleFrom="md" style={{ overflowX: 'auto' }}>
<Table highlightOnHover striped verticalSpacing="sm" miw={800}>
<TableThead>
<TableTr>
<TableTh w="25%">Nama Prestasi</TableTh>
<TableTh w="25%">Deskripsi</TableTh>
<TableTh w="25%">Kategori</TableTh>
<TableTh w="25%" ta="center">Aksi</TableTh>
<TableTh>Nama Prestasi</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Kategori</TableTh>
<TableTh ta="center">Aksi</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.length > 0 ? (
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd w="25%">
<TableTd style={{ maxWidth: 250 }}>
<Text truncate="end" fz="md" lh={1.5}>
{item.name}
</Text>
</TableTd>
<TableTd w="25%">
<TableTd style={{ maxWidth: 250 }}>
<Text lineClamp={1} fz="md" c="dimmed" lh={1.5} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</TableTd>
<TableTd w="25%">
<TableTd>
<Text truncate="end" fz="md" lh={1.5}>
{item.kategori?.name || 'Tidak ada kategori'}
</Text>
</TableTd>
<TableTd w="25%" ta="center">
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() => router.push(`/admin/landing-page/prestasi-desa/list-prestasi-desa/${item.id}`)}
>
Detail
</Button>
<TableTd ta="center">
<Center>
<Button
size="sm"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() => router.push(`/admin/landing-page/prestasi-desa/list-prestasi-desa/${item.id}`)}
>
Detail
</Button>
</Center>
</TableTd>
</TableTr>
))
@@ -127,39 +130,48 @@ function ListPrestasi({ search }: { search: string }) {
{/* Mobile Cards */}
<Stack hiddenFrom="md" gap="xs">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="sm" radius="sm">
<Stack gap={4}>
<Text fz="sm" fw={600} lh={1.4}>
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="sm" radius="sm">
<Stack gap={"xs"}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Nama Prestasi</Text>
<Text fz="sm" fw={500} lh={1.4} lineClamp={2}>
{item.name}
</Text>
<Text fz="xs" c="dimmed" lh={1.5} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
<Text fz="xs" c="dimmed" lh={1.4}>
Kategori: {item.kategori?.name || 'Tidak ada kategori'}
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Deskripsi</Text>
<Text fz="sm" fw={500} lh={1.5} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Kategori</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.kategori?.name || 'Tidak ada kategori'}
</Text>
<Group justify="flex-end" mt="xs">
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={14} />}
onClick={() => router.push(`/admin/landing-page/prestasi-desa/list-prestasi-desa/${item.id}`)}
>
Detail
</Button>
</Group>
</Stack>
</Paper>
))
) : (
<Center py="md">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data prestasi
</Text>
</Center>
)}
</Box>
<Group justify="flex-end" mt="xs">
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={14} />}
onClick={() => router.push(`/admin/landing-page/prestasi-desa/list-prestasi-desa/${item.id}`)}
>
Detail
</Button>
</Group>
</Stack>
</Paper>
))
) : (
<Center py="md">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data prestasi
</Text>
</Center>
)}
</Stack>
</Paper>

View File

@@ -20,7 +20,7 @@ import {
Text,
Title,
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -48,6 +48,7 @@ function MediaSosial() {
function ListMediaSosial({ search }: { search: string }) {
const stateMediaSosial = useProxy(profileLandingPageState.mediaSosial);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
const getIconSource = (item: any) => {
if (item.image?.link) return item.image.link;
@@ -66,8 +67,8 @@ function ListMediaSosial({ search }: { search: string }) {
} = stateMediaSosial.findMany;
useShallowEffect(() => {
load(page, 10, search);
}, [page, search]);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
const filteredData = data || [];
@@ -195,12 +196,15 @@ function ListMediaSosial({ search }: { search: string }) {
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="sm" radius="md">
<Group justify="space-between" wrap="nowrap" align='center'>
<Box>
<Text fw={600} fz="sm" lh={1.45}>
<Text fz="sm" fw={600} lh={1.4}>Nama Media Sosial / Kontak</Text>
<Text fw={500} fz="sm" lh={1.45}>
{item.name}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Gambar</Text>
</Box>
<Box w={40} h={40} style={{ borderRadius: 6, overflow: 'hidden' }}>
{(() => {
const src = getIconSource(item);
@@ -217,8 +221,8 @@ function ListMediaSosial({ search }: { search: string }) {
return <Box bg={colors['blue-button']} w="100%" h="100%" />;
})()}
</Box>
</Group>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Link / No. Telepon</Text>
<a
href={item.link}
target="_blank"

View File

@@ -1,7 +1,7 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -29,12 +29,13 @@ function ProgramInovasi() {
function ListProgramInovasi({ search }: { search: string }) {
const stateProgramInovasi = useProxy(profileLandingPageState.programInovasi);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
const { data, page, totalPages, loading, load } = stateProgramInovasi.findMany;
useShallowEffect(() => {
load(page, 10, search);
}, [page, search]);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
const filteredData = data || [];
@@ -135,15 +136,22 @@ function ListProgramInovasi({ search }: { search: string }) {
>
<Stack gap={6}>
{/* Title */}
<Text fw={600}>{item.name}</Text>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Nama Program</Text>
<Text fw={500} lh={1.4}>{item.name}</Text>
</Box>
{/* Description */}
<Text fz="sm" c="gray.7" lineClamp={2}>
{item.description || '-'}
<Box>
<Text fz="sm" fw={600} lh={1.4}>Deskripsi</Text>
<Text fz="sm" c="gray.7" lineClamp={2}>
{item.description || '-'}
</Text>
</Box>
{/* Link */}
<Box>
<Text fz="sm" fw={600} lh={1.4}>Link</Text>
<a
href={item.link}
target="_blank"