Compare commits

..

1 Commits

Author SHA1 Message Date
af60bcd6fc Fix QC Kak Inno Tgl 17
Fix QC Kak Ayu Tgl 17
Fix UI Admin Mobile Menu PPID
Search Admin Menu Landing Page & Menu PPID
2025-12-18 17:25:22 +08:00
62 changed files with 2494 additions and 1008 deletions

View File

@@ -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 { IconBuildingStore, IconFileText, IconSparkles, IconUsers, IconUsersPlus } from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
@@ -72,35 +72,76 @@ function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
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", // ✅ biar nggak nempel ke tepi
}}
>
{tabs.map((tab, i) => (
<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}
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>
))}
</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

View File

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

View File

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

View File

@@ -26,6 +26,7 @@ import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import stateDashboardBerita from '../../../_state/desa/berita';
import { useDebouncedValue } from '@mantine/hooks';
function KategoriBerita() {
const [search, setSearch] = useState('');
@@ -48,6 +49,7 @@ function ListKategoriBerita({ 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,
@@ -58,8 +60,8 @@ function ListKategoriBerita({ search }: { search: string }) {
} = listDataState.findMany;
useEffect(() => {
load(page, 10, search);
}, [page, search]);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
const handleDelete = () => {
if (selectedId) {
@@ -81,77 +83,84 @@ function ListKategoriBerita({ search }: { search: string }) {
}
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 Kategori Berita</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() =>
router.push('/admin/desa/berita/kategori-berita/create')
}
>
Tambah Baru
</Button>
<Box py={{ base: 'sm', md: 'lg' }}>
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
<Group justify="space-between" mb={{ base: 'md', md: 'lg' }}>
<Title order={2} lh={1.2}>
Daftar Kategori Berita
</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() =>
router.push('/admin/desa/berita/kategori-berita/create')
}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover miw={0}>
<TableThead>
<TableTr>
<TableTh style={{ width: '10%' }}>No</TableTh>
<TableTh style={{ width: '50%' }}>Nama</TableTh>
<TableTh style={{ width: '20%' }}>Edit</TableTh>
<TableTh style={{ width: '20%' }}>Hapus</TableTh>
<TableTh w="50%">
<Text fz="sm" fw={600} lh={1.4}>Kategori</Text>
</TableTh>
<TableTh w="20%">
<Text fz="sm" fw={600} lh={1.4} ta="center">Edit</Text>
</TableTh>
<TableTh w="20%">
<Text fz="sm" fw={600} lh={1.4} ta="center">Hapus</Text>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.length > 0 ? (
filteredData.map((item, index) => (
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Text fz="sm">{index + 1}</Text>
</TableTd>
<TableTd>
<Text fw={500} truncate="end" lineClamp={1}>
<Text fz="sm" fw={500} lh={1.45} truncate="end">
{item.name}
</Text>
</TableTd>
<TableTd>
<Button
variant="light"
color="green"
onClick={() =>
router.push(
`/admin/desa/berita/kategori-berita/${item.id}`
)
}
>
<IconEdit size={18} />
</Button>
<TableTd ta="center">
<Button
variant="light"
color="green"
onClick={() =>
router.push(
`/admin/desa/berita/kategori-berita/${item.id}`
)
}
size="compact-sm"
>
<IconEdit size={16} />
</Button>
</TableTd>
<TableTd>
<Button
variant="light"
color="red"
disabled={listDataState.delete.loading}
onClick={() => {
setSelectedId(item.id);
setModalHapus(true);
}}
>
<IconTrash size={18} />
</Button>
<TableTd ta="center">
<Button
variant="light"
color="red"
disabled={listDataState.delete.loading}
onClick={() => {
setSelectedId(item.id);
setModalHapus(true);
}}
size="compact-sm"
>
<IconTrash size={16} />
</Button>
</TableTd>
</TableTr>
))
) : (
<TableTr>
<TableTd colSpan={4}>
<Center py={20}>
<Text color="dimmed">
<Center py={24}>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data kategori berita yang cocok
</Text>
</Center>
@@ -161,22 +170,70 @@ function ListKategoriBerita({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Cards */}
<Stack hiddenFrom="md" gap="xs" mt="md">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder radius="md" p="sm" bg="white">
<Box flex={1} ml="md">
<Text fz="sm" fw={600} lh={1.4}>Kategori</Text>
<Text fz="sm" fw={500} lh={1.45} truncate>
{item.name}
</Text>
</Box>
<Group mt="sm" justify="flex-end" gap="xs">
<Button
variant="light"
color="green"
size="compact-xs"
onClick={() =>
router.push(
`/admin/desa/berita/kategori-berita/${item.id}`
)
}
>
<IconEdit size={14} />
</Button>
<Button
variant="light"
color="red"
size="compact-xs"
disabled={listDataState.delete.loading}
onClick={() => {
setSelectedId(item.id);
setModalHapus(true);
}}
>
<IconTrash size={14} />
</Button>
</Group>
</Paper>
))
) : (
<Center py={32}>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data kategori berita yang cocok
</Text>
</Center>
)}
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10, search);
window.scrollTo({ top: 0, behavior: 'smooth' });
}}
total={totalPages}
mt="md"
mb="md"
color="blue"
radius="md"
/>
</Center>
{totalPages > 1 && (
<Center mt={{ base: 'lg', md: 'xl' }}>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10, search);
window.scrollTo({ top: 0, behavior: 'smooth' });
}}
total={totalPages}
color="blue"
radius="md"
/>
</Center>
)}
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
@@ -189,4 +246,4 @@ function ListKategoriBerita({ search }: { search: string }) {
);
}
export default KategoriBerita;
export default KategoriBerita;

View File

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

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ import {
Text,
Title
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -45,16 +45,17 @@ function Berita() {
function ListBerita({ search }: { search: string }) {
const beritaState = useProxy(stateDashboardBerita);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load } = beritaState.berita.findMany;
useShallowEffect(() => {
load(page, 10, search);
}, [page, search]);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
if (loading || !data) {
return (
<Stack py={10}>
<Stack py="md">
<Skeleton height={600} radius="md" />
</Stack>
);
@@ -63,64 +64,67 @@ function ListBerita({ search }: { search: string }) {
const filteredData = data || [];
return (
<Box py={10}>
<Box py="md">
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
<Group justify="space-between" mb="md">
<Title order={4}>Daftar Berita</Title>
<Button
leftSection={<IconCircleDashedPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/berita/list-berita/create')}
>
Tambah Baru
</Button>
<Title order={2} visibleFrom="md">Daftar Berita</Title>
<Title order={3} hiddenFrom="md">Daftar Berita</Title>
<Button
leftSection={<IconCircleDashedPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/berita/list-berita/create')}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover miw={0}>
<TableThead>
<TableTr>
<TableTh style={{ width: '30%' }}>Judul</TableTh>
<TableTh style={{ width: '20%' }}>Kategori</TableTh>
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
<TableTh w="50%">Judul</TableTh>
<TableTh w="30%">Kategori</TableTh>
<TableTh w="20%">Aksi</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.length > 0 ? (
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd style={{ width: '30%' }}>
<Box w={150}>
<Text fw={500} truncate="end" lineClamp={1}>
{item.judul}
</Text>
</Box>
<TableTd>
<Text fz="md" fw={600} lh={1.45} truncate="end">
{item.judul}
</Text>
</TableTd>
<TableTd style={{ width: '20%' }}>
<Text fz="sm" c="dimmed">
<TableTd>
<Text fz="sm" c="dimmed" lh={1.45}>
{item.kategoriBerita?.name || '-'}
</Text>
</TableTd>
<TableTd style={{ width: '15%' }}>
<TableTd>
<Button
variant="light"
color="blue"
onClick={() =>
router.push(`/admin/desa/berita/list-berita/${item.id}`)
}
fz="sm"
px="sm"
h={36}
>
<IconDeviceImacCog size={20} />
<Text ml={5}>Detail</Text>
<IconDeviceImacCog size={18} />
<Text ml="xs">Detail</Text>
</Button>
</TableTd>
</TableTr>
))
) : (
<TableTr>
<TableTd colSpan={4}>
<Center py={20}>
<Text color="dimmed">
<TableTd colSpan={3}>
<Center py="xl">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data berita yang cocok
</Text>
</Center>
@@ -130,6 +134,52 @@ function ListBerita({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Cards */}
<Stack hiddenFrom="md" gap="sm" mt="sm">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="md" radius="md">
<Stack gap={4}>
<Text fz="sm" fw={600} lh={1.4} c="dimmed">
Judul
</Text>
<Text fz="sm" fw={500} lh={1.45}>
{item.judul}
</Text>
<Text fz="sm" fw={600} lh={1.4} c="dimmed" mt="xs">
Kategori
</Text>
<Text fz="sm" lh={1.45} fw={500}>
{item.kategoriBerita?.name || '-'}
</Text>
<Button
variant="light"
color="blue"
fullWidth
mt="sm"
onClick={() =>
router.push(`/admin/desa/berita/list-berita/${item.id}`)
}
fz="sm"
h={36}
>
<IconDeviceImacCog size={18} />
<Text ml="xs">Detail</Text>
</Button>
</Stack>
</Paper>
))
) : (
<Center py="xl">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data berita yang cocok
</Text>
</Center>
)}
</Stack>
</Paper>
<Center>
@@ -150,4 +200,4 @@ function ListBerita({ search }: { search: string }) {
);
}
export default Berita;
export default Berita;

View File

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

View File

@@ -45,7 +45,7 @@ function DetailFoto() {
const imageUrl = data.imageGalleryFoto?.link;
return (
<Box py={10}>
<Box px={{ base: 0, md: 'xs' }} py="xs">
<Button
variant="subtle"
onClick={() => router.back()}
@@ -59,7 +59,7 @@ function DetailFoto() {
withBorder
// Gunakan max-width agar tidak terlalu lebar di desktop
maw={800}
w="100%"
w={{ base: "100%", md: "70%" }}
bg={colors['white-1']}
p="lg"
radius="md"

View File

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

View File

@@ -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,6 +45,7 @@ function Foto() {
function ListFoto({ search }: { search: string }) {
const FotoState = useProxy(stateGallery.foto)
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const {
data,
@@ -55,76 +56,81 @@ function ListFoto({ search }: { search: string }) {
} = FotoState.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: 'md', md: 'lg' }}>
<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 Foto</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/gallery/foto/create')}
>
Tambah Baru
</Button>
<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={2} lh={1.2}>Daftar Foto</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/gallery/foto/create')}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: "auto" }}>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover>
<TableThead>
<TableTr>
<TableTh style={{ width: '25%' }}>Judul Foto</TableTh>
<TableTh style={{ width: '20%' }}>Tanggal</TableTh>
<TableTh style={{ width: '30%' }}>Deskripsi</TableTh>
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
<TableTh>Judul Foto</TableTh>
<TableTh>Tanggal</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Aksi</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.length > 0 ? (
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd style={{ width: '25%' }}>
<Box w={200}>
<Text fw={500} truncate="end" lineClamp={1}>{item.name}</Text>
</Box>
<TableTd>
<Text fz="md" fw={500} lh={1.45} truncate="end" lineClamp={1}>
{item.name}
</Text>
</TableTd>
<TableTd style={{ width: '20%' }}>
<Box w={200}>
<Text fz="sm" c="dimmed">
<TableTd>
<Text fz="sm" c="dimmed" lh={1.45}>
{new Date(item.createdAt).toLocaleDateString('id-ID', {
day: 'numeric',
month: 'long',
year: 'numeric',
})}
</Text>
</Box>
</TableTd>
<TableTd style={{ width: '30%' }}>
<Box w={200}>
<Text fz="sm" truncate="end" lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
<TableTd>
<Text
fz="sm"
lh={1.45}
truncate="end"
lineClamp={1}
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
/>
</TableTd>
<TableTd style={{ width: '15%' }}>
<TableTd>
<Button
variant="light"
color="blue"
size="xs"
onClick={() => router.push(`/admin/desa/gallery/foto/${item.id}`)}
>
<IconDeviceImac size={20} />
<Text ml={5}>Detail</Text>
<IconDeviceImac size={16} />
<Text ml={5} fz="sm" fw={500}>Detail</Text>
</Button>
</TableTd>
</TableTr>
@@ -133,7 +139,7 @@ function ListFoto({ search }: { search: string }) {
<TableTr>
<TableTd colSpan={4}>
<Center py={20}>
<Text c="dimmed">Tidak ada foto yang cocok</Text>
<Text c="dimmed" fz="sm" lh={1.4}>Tidak ada foto yang cocok</Text>
</Center>
</TableTd>
</TableTr>
@@ -141,7 +147,54 @@ function ListFoto({ 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 radius="sm" p="md">
<Stack gap="xs">
<Box>
<Text fz="sm" fw={600} lh={1.4}>Judul Foto</Text>
<Text fz="sm" fw={500} lh={1.45}>{item.name}</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Tanggal</Text>
<Text fz="sm" fw={500} lh={1.45} c="dimmed">
{new Date(item.createdAt).toLocaleDateString('id-ID', {
day: 'numeric',
month: 'long',
year: 'numeric',
})}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Deskripsi</Text>
<Text fz="sm" fw={500} lh={1.45} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
<Button
variant="light"
color="blue"
size="xs"
fullWidth
onClick={() => router.push(`/admin/desa/gallery/foto/${item.id}`)}
>
<IconDeviceImac size={16} />
<Text ml={5} fz="sm" fw={500}>Detail</Text>
</Button>
</Stack>
</Paper>
))
) : (
<Center py={20}>
<Text c="dimmed" fz="sm" lh={1.4}>Tidak ada foto yang cocok</Text>
</Center>
)}
</Stack>
</Box>
</Paper>
<Center>
<Pagination
value={page}
@@ -160,4 +213,4 @@ function ListFoto({ search }: { search: string }) {
);
}
export default Foto;
export default Foto;

View File

@@ -118,7 +118,7 @@ function EditVideo() {
const embedLink = convertYoutubeUrlToEmbed(formData.linkVideo);
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Group mb="md">
<Button
variant="subtle"

View File

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

View File

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

View File

@@ -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,6 +45,7 @@ function Video() {
function ListVideo({ search }: { search: string }) {
const videoState = useProxy(stateGallery.video)
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const {
data,
@@ -55,75 +56,77 @@ function ListVideo({ search }: { search: string }) {
} = videoState.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={20}>
<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 Video</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/gallery/video/create')}
>
Tambah Baru
</Button>
<Box py={20}>
<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={2} lh={1.2}>
Daftar Video
</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/gallery/video/create')}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: "auto" }}>
<Table highlightOnHover>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover w="100%">
<TableThead>
<TableTr>
<TableTh style={{ width: '25%' }}>Judul Video</TableTh>
<TableTh style={{ width: '20%' }}>Tanggal</TableTh>
<TableTh style={{ width: '30%' }}>Deskripsi</TableTh>
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
<TableTh>Judul Video</TableTh>
<TableTh>Tanggal</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Aksi</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.length > 0 ? (
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd style={{ width: '25%' }}>
<Box w={200}>
<Text fw={500} truncate="end" lineClamp={1}>{item.name}</Text>
</Box>
<TableTd>
<Text fz="md" fw={500} lh={1.45} truncate="end" lineClamp={1}>
{item.name}
</Text>
</TableTd>
<TableTd style={{ width: '20%' }}>
<Box w={200}>
<Text fz="sm" c="dimmed">
<TableTd>
<Text fz="sm" c="dimmed" lh={1.45}>
{new Date(item.createdAt).toLocaleDateString('id-ID', {
day: 'numeric',
month: 'long',
year: 'numeric',
})}
</Text>
</Box>
</TableTd>
<TableTd style={{ width: '30%' }}>
<Box w={200}>
<Text fz="sm" truncate="end" lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
<TableTd>
<Text fz="sm" lh={1.45} truncate="end" lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</TableTd>
<TableTd style={{ width: '15%' }}>
<TableTd>
<Button
variant="light"
color="blue"
onClick={() => router.push(`/admin/desa/gallery/video/${item.id}`)}
fz="sm"
px="xs"
>
<IconDeviceImac size={20} />
<IconDeviceImac size={18} />
<Text ml={5}>Detail</Text>
</Button>
</TableTd>
@@ -132,8 +135,10 @@ function ListVideo({ search }: { search: string }) {
) : (
<TableTr>
<TableTd colSpan={4}>
<Center py={20}>
<Text c="dimmed">Tidak ada video yang cocok</Text>
<Center py={24}>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada video yang cocok
</Text>
</Center>
</TableTd>
</TableTr>
@@ -141,23 +146,74 @@ function ListVideo({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Cards */}
<Stack hiddenFrom="md" gap="xs" mt="sm">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} p="sm" withBorder radius="sm">
<Stack gap={4}>
<Box>
<Text fz="xs" fw={600} lh={1.4}>Judul Video</Text>
<Text fz="sm" fw={500} lh={1.45}>
{item.name}
</Text>
</Box>
<Box>
<Text fz="xs" fw={600} lh={1.4}>Tanggal</Text>
<Text fz="sm" fw={500} lh={1.45}>
{new Date(item.createdAt).toLocaleDateString('id-ID', {
day: 'numeric',
month: 'long',
year: 'numeric',
})}
</Text>
</Box>
<Box>
<Text fz="xs" fw={600} lh={1.4}>Deskripsi</Text>
<Text fz="sm" lineClamp={5} fw={500} lh={1.45} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
<Box>
<Button
variant="light"
color="blue"
fullWidth
onClick={() => router.push(`/admin/desa/gallery/video/${item.id}`)}
fz="sm"
>
<IconDeviceImac size={18} />
<Text ml={5}>Detail</Text>
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Center py={24}>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada video yang cocok
</Text>
</Center>
)}
</Stack>
</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 mt="xl">
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10)
window.scrollTo({ top: 0, behavior: 'smooth' })
}}
total={totalPages}
color="blue"
radius="md"
/>
</Center>
)}
</Box>
);
}
export default Video;
export default Video;

View File

@@ -115,7 +115,7 @@ function EditAjukanPermohonan() {
};
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">

View File

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

View File

@@ -24,6 +24,7 @@ import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import stateLayananDesa from '../../../_state/desa/layananDesa';
import { useDebouncedValue } from '@mantine/hooks';
function AjukanPermohonan() {
const [search, setSearch] = useState("");
@@ -44,6 +45,7 @@ function AjukanPermohonan() {
function ListAjukanPermohonan({ search }: { search: string }) {
const AjukanPermohonanState = useProxy(stateLayananDesa.ajukanPermohonan);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
const {
data,
@@ -54,58 +56,56 @@ function ListAjukanPermohonan({ search }: { search: string }) {
} = AjukanPermohonanState.findMany;
useEffect(() => {
load(page, 10, search);
}, [page, search]);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
// Loading state
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}>List Ajukan Permohonan</Title>
<Box style={{ overflowX: "auto" }}>
<Table highlightOnHover>
<Box py={{ base: 'sm', md: 'md' }}>
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
<Title order={2} lh={1.2} mb={{ base: 'md', md: 'lg' }}>
List Ajukan Permohonan
</Title>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover miw={0}>
<TableThead>
<TableTr>
<TableTh style={{ width: '30%' }}>Nama</TableTh>
<TableTh style={{ width: '45%' }}>Alamat</TableTh>
<TableTh style={{ width: '15%' }}>NIK</TableTh>
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
<TableTh fz="sm" fw={600} lh={1.4}>Nama</TableTh>
<TableTh fz="sm" fw={600} lh={1.4}>Alamat</TableTh>
<TableTh fz="sm" fw={600} lh={1.4}>NIK</TableTh>
<TableTh fz="sm" fw={600} lh={1.4}>Aksi</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{data.length > 0 ? (
data.map((item) => (
<TableTr key={item.id}>
<TableTd style={{ width: '30%' }}>
<Box w={200}>
<Text fw={500} truncate="end" lineClamp={1}>
{item.nama}
</Text>
</Box>
<TableTd>
<Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
{item.nama}
</Text>
</TableTd>
<TableTd style={{ width: '45%' }}>
<Box w={200}>
<Text fw={500} truncate="end" lineClamp={1}>
{item.alamat}
</Text>
</Box>
<TableTd>
<Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
{item.alamat}
</Text>
</TableTd>
<TableTd style={{ width: '45%' }}>
<Box w={200}>
<Text fw={500} truncate="end" lineClamp={1}>
{item.nik}
</Text>
</Box>
<TableTd>
<Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
{item.nik}
</Text>
</TableTd>
<TableTd style={{ width: '15%' }}>
<TableTd>
<Button
size="xs"
radius="md"
@@ -123,9 +123,11 @@ function ListAjukanPermohonan({ search }: { search: string }) {
))
) : (
<TableTr>
<TableTd colSpan={3}>
<Center py={20}>
<Text color="dimmed">Tidak ada data ajukan permohonan yang cocok</Text>
<TableTd colSpan={4}>
<Center py="xl">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data ajukan permohonan yang cocok
</Text>
</Center>
</TableTd>
</TableTr>
@@ -133,23 +135,71 @@ function ListAjukanPermohonan({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Card View */}
<Box hiddenFrom="md">
<Stack gap="md">
{data.length > 0 ? (
data.map((item) => (
<Paper key={item.id} withBorder p="md" radius="md" shadow="xs">
<Stack gap={4}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Nama</Text>
<Text fz="sm" fw={500} lh={1.5}>{item.nama}</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>Alamat</Text>
<Text fz="sm" fw={500} lh={1.5}>{item.alamat}</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>NIK</Text>
<Text fz="sm" fw={500} lh={1.5}>{item.nik}</Text>
</Box>
<Box>
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() =>
router.push(`/admin/desa/layanan/ajukan_permohonan/${item.id}`)
}
fullWidth
>
Detail
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Center py="xl">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data ajukan permohonan yang cocok
</Text>
</Center>
)}
</Stack>
</Box>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10, search);
window.scrollTo({ top: 0, behavior: 'smooth' });
}}
total={totalPages}
mt="md"
mb="md"
color="blue"
radius="md"
/>
</Center>
{totalPages > 1 && (
<Center mt="md">
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10, search);
window.scrollTo({ top: 0, behavior: 'smooth' });
}}
total={totalPages}
color="blue"
radius="md"
/>
</Center>
)}
</Box>
);
}
export default AjukanPermohonan;
export default AjukanPermohonan;

View File

@@ -108,7 +108,7 @@ function EditPelayananPendudukNonPermanent() {
};
return (
<Box>
<Box px={{ base: 0, md: 'xs' }} py="xs">
<Stack gap="xs">
<Group mb="md">
<Button

View File

@@ -45,24 +45,28 @@ function PelayananPendudukNonPermanent() {
{/* Header */}
<Grid align="center">
<GridCol span={{ base: 12, md: 11 }}>
<Title order={3} c={colors['blue-button']}>
<Title
order={3}
lh={1.2}
c={colors['blue-button']}
>
Preview Pelayanan Penduduk Non Permanen
</Title>
</GridCol>
<GridCol span={{ base: 12, md: 1 }}>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() =>
router.push(
`/admin/desa/layanan/pelayanan_penduduk_non_permanent/${data.id}`
)
}
>
Edit
</Button>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() =>
router.push(
`/admin/desa/layanan/pelayanan_penduduk_non_permanent/${data.id}`
)
}
>
Edit
</Button>
</GridCol>
</Grid>
@@ -70,14 +74,14 @@ function PelayananPendudukNonPermanent() {
<Paper p="xl" bg={'white'} withBorder radius="md" shadow="xs">
<Box px={{ base: 0, md: 50 }} pb="xl">
<Center>
<Text
<Title
order={2}
lh={1.2}
ta="center"
fz={{ base: '1.2rem', md: '1.8rem' }}
fw="bold"
c={colors['blue-button']}
>
{data.name}
</Text>
</Title>
</Center>
<Divider my="md" color={colors['blue-button']} />
@@ -86,9 +90,11 @@ function PelayananPendudukNonPermanent() {
<Text
py={10}
ta="justify"
fz={{ base: '1rem', md: '1.2rem' }}
fz={{ base: 'sm', md: 'md' }}
lh={{ base: 1.5, md: 1.55 }}
c="dark"
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
style={{wordBreak: "break-word", whiteSpace: "normal"}}
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
/>
</Box>
</Box>
@@ -98,4 +104,4 @@ function PelayananPendudukNonPermanent() {
);
}
export default PelayananPendudukNonPermanent;
export default PelayananPendudukNonPermanent;

View File

@@ -123,7 +123,7 @@ function EditPelayananPerizinanBerusaha() {
}
return (
<Box>
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Stack gap="xs">
{/* Header */}
<Group mb="md">

View File

@@ -41,8 +41,7 @@ function PerizinanBerusaha() {
const loadData = async () => {
try {
setLoading(true);
// You should get the ID from your router query or params
const id = 'edit'; // Replace with actual ID or get from URL params
const id = 'edit';
await pelayananPerizinanBerusaha.findById.load(id);
} catch (err) {
setError('Gagal memuat data');
@@ -66,7 +65,7 @@ function PerizinanBerusaha() {
if (error || !pelayananPerizinanBerusaha.findById.data) {
return (
<Center h={200}>
<Text>{error || 'Data tidak ditemukan'}</Text>
<Text c="dimmed">{error || 'Data tidak ditemukan'}</Text>
</Center>
);
}
@@ -79,24 +78,24 @@ function PerizinanBerusaha() {
{/* Header */}
<Grid align="center">
<GridCol span={{ base: 12, md: 11 }}>
<Title order={3} c={colors['blue-button']}>
<Title order={3} c={colors['blue-button']} lh={1.2}>
Preview Pelayanan Perizinan Berusaha
</Title>
</GridCol>
<GridCol span={{ base: 12, md: 1 }}>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() =>
router.push(
`/admin/desa/layanan/pelayanan_perizinan_berusaha/${data.id}`
)
}
>
Edit
</Button>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() =>
router.push(
`/admin/desa/layanan/pelayanan_perizinan_berusaha/${data.id}`
)
}
>
Edit
</Button>
</GridCol>
</Grid>
@@ -104,38 +103,40 @@ function PerizinanBerusaha() {
<Paper p="xl" bg={'white'} withBorder radius="md" shadow="xs">
<Box px={{ base: 0, md: 50 }} pb="xl">
<Center>
<Text
<Title
order={3}
ta="center"
fz={{ base: '1.2rem', md: '1.8rem' }}
fw="bold"
c={colors['blue-button']}
lh={1.15}
>
{data.name}
</Text>
</Title>
</Center>
<Divider my="md" color={colors['blue-button']} />
<Box mt="lg">
<Text
py={10}
ta="justify"
fz={{ base: '1rem', md: '1.2rem' }}
py="xs"
ta={{ base: "left", md: "justify" }}
fz={{ base: 'sm', md: 'md' }}
lh={1.55}
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
<Text
py={10}
fz={{ base: '1rem', md: '1.2rem' }}
fw="bold"
py="xs"
fz={{ base: 'sm', md: 'md' }}
fw={700}
c={colors['blue-button']}
lh={1.5}
>
Proses pendaftaran NIB melalui OSS mencakup beberapa langkah
umum:
</Text>
<Box p="xl" w="100%">
<Box p="xl" w="100%" visibleFrom='md'>
<Stepper
active={active}
onStepClick={setActive}
@@ -143,28 +144,115 @@ function PerizinanBerusaha() {
styles={{
separator: { marginLeft: 25 },
step: { padding: '12px 0' },
stepLabel: {
fontSize: 'var(--mantine-font-size-sm)',
fontWeight: 700,
lineHeight: 1.4,
},
stepDescription: {
fontSize: 'var(--mantine-font-size-xs)',
lineHeight: 1.4,
},
}}
>
<StepperStep label="Langkah Pertama" description="Pendaftaran Akun">
Pendaftaran akun pada portal OSS
<Text fz="sm" lh={1.5}>
Pendaftaran akun pada portal OSS
</Text>
</StepperStep>
<StepperStep label="Langkah Kedua" description="Pengisian Data Perusahaan">
Mengisi informasi perusahaan, termasuk data pemegang saham, alamat perusahaan, dan lainnya
<Text fz="sm" lh={1.5}>
Mengisi informasi perusahaan, termasuk data pemegang saham, alamat perusahaan, dan lainnya
</Text>
</StepperStep>
<StepperStep label="Langkah Ketiga" description="Pemilihan KBLI">
Memilih KBLI dengan jenis usaha yang akan didaftarkan
<Text fz="sm" lh={1.5}>
Memilih KBLI dengan jenis usaha yang akan didaftarkan
</Text>
</StepperStep>
<StepperStep label="Langkah Keempat" description="Pengunggahan Dokumen">
Mengunggah dokumen-dokumen yang diperlukan, seperti akta pendirian perusahaan, surat izin usaha, dan dokumen lainnya sesuai dengan ketentuan yang berlaku
<Text fz="sm" lh={1.5}>
Mengunggah dokumen-dokumen yang diperlukan, seperti akta pendirian perusahaan, surat izin usaha, dan dokumen lainnya sesuai dengan ketentuan yang berlaku
</Text>
</StepperStep>
<StepperStep label="Langkah Kelima" description="Verifikasi dan Persetujuan">
Proses verifikasi dan persetujuan oleh instansi terkait
<Text fz="sm" lh={1.5}>
Proses verifikasi dan persetujuan oleh instansi terkait
</Text>
</StepperStep>
<StepperStep label="Langkah Keenam" description="Penerimaan NIB">
Jika proses sebelumnya berhasil, perusahaan akan menerima NIB sebagai identitas resmi usaha anda
<Text fz="sm" lh={1.5}>
Jika proses sebelumnya berhasil, perusahaan akan menerima NIB sebagai identitas resmi usaha anda
</Text>
</StepperStep>
<StepperCompleted>
Selesai, anda telah mengikuti proses pendaftaran NIB melalui OSS
<Text fz="sm" lh={1.5}>
Selesai, anda telah mengikuti proses pendaftaran NIB melalui OSS
</Text>
</StepperCompleted>
</Stepper>
<Group justify="center" mt="xl">
<Button variant="default" onClick={prevStep}>
Back
</Button>
<Button onClick={nextStep}>Next step</Button>
</Group>
</Box>
<Box p="xl" w="100%" hiddenFrom='md'>
<Stepper
active={active}
onStepClick={setActive}
orientation="vertical"
styles={{
separator: { marginLeft: 25 },
step: { padding: '12px 0' },
stepLabel: {
fontSize: 'var(--mantine-font-size-sm)',
fontWeight: 700,
lineHeight: 1.4,
},
stepDescription: {
fontSize: 'var(--mantine-font-size-xs)',
lineHeight: 1.4,
},
}}
>
<StepperStep label="Langkah Pertama" description="Pendaftaran Akun">
<Text fz="sm" lh={1.5}>
</Text>
</StepperStep>
<StepperStep label="Langkah Kedua" description="Pengisian Data Perusahaan">
<Text fz="sm" lh={1.5}>
</Text>
</StepperStep>
<StepperStep label="Langkah Ketiga" description="Pemilihan KBLI">
<Text fz="sm" lh={1.5}>
</Text>
</StepperStep>
<StepperStep label="Langkah Keempat" description="Pengunggahan Dokumen">
<Text fz="sm" lh={1.5}>
</Text>
</StepperStep>
<StepperStep label="Langkah Kelima" description="Verifikasi dan Persetujuan">
<Text fz="sm" lh={1.5}>
</Text>
</StepperStep>
<StepperStep label="Langkah Keenam" description="Penerimaan NIB">
<Text fz="sm" lh={1.5}>
</Text>
</StepperStep>
<StepperCompleted>
<Text fz="sm" lh={1.5}>
</Text>
</StepperCompleted>
</Stepper>
@@ -177,9 +265,10 @@ function PerizinanBerusaha() {
</Box>
<Text
py={35}
ta="justify"
fz={{ base: '1rem', md: '1.2rem' }}
py="md"
ta={{ base: "left", md: "justify" }}
fz={{ base: 'sm', md: 'md' }}
lh={1.55}
>
Penting untuk diingat bahwa prosedur dan persyaratan dapat
berubah seiring waktu. Untuk informasi yang lebih akurat dan
@@ -203,5 +292,4 @@ function PerizinanBerusaha() {
);
}
export default PerizinanBerusaha;
export default PerizinanBerusaha;

View File

@@ -64,7 +64,7 @@ const FileUploader: React.FC<FileUploaderProps> = ({
};
return (
<Box>
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Text fw="bold" fz="sm" mb={6}>
{title}
</Text>

View File

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

View File

@@ -87,7 +87,7 @@ function CreateSuratKeterangan() {
};
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">

View File

@@ -17,7 +17,7 @@ import {
TableThead,
TableTr,
Text,
Title
Title,
} from '@mantine/core';
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -25,9 +25,10 @@ import { useEffect, useMemo, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import stateLayananDesa from '../../../_state/desa/layananDesa';
import { useDebouncedValue } from '@mantine/hooks';
function SuratKeterangan() {
const [search, setSearch] = useState("");
const [search, setSearch] = useState('');
return (
<Box>
<HeaderSearch
@@ -45,6 +46,7 @@ function SuratKeterangan() {
function ListSuratKeterangan({ search }: { search: string }) {
const suratKeteranganState = useProxy(stateLayananDesa.suratKeterangan);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const {
data,
@@ -55,72 +57,80 @@ function ListSuratKeterangan({ search }: { search: string }) {
} = suratKeteranganState.findMany;
useEffect(() => {
load(page, 10, search);
}, [page, search]);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
const filteredData = useMemo(() => {
if (!data) return [];
const keyword = search.toLowerCase();
return data.filter(item =>
item.name?.toLowerCase().includes(keyword) ||
item.deskripsi?.toLowerCase().includes(keyword)
const keyword = debouncedSearch.toLowerCase();
return data.filter(
(item) =>
item.name?.toLowerCase().includes(keyword) ||
item.deskripsi?.toLowerCase().includes(keyword)
);
}, [data, search]);
}, [data, debouncedSearch]);
// Loading state
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}>List Surat Keterangan</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() =>
router.push('/admin/desa/layanan/pelayanan_surat_keterangan/create')
}
>
Tambah Baru
</Button>
<Box py={{ base: 'sm', 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={2} lh={1.2}>
List Surat Keterangan
</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() =>
router.push('/admin/desa/layanan/pelayanan_surat_keterangan/create')
}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: "auto" }}>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover>
<TableThead>
<TableTr>
<TableTh style={{ width: '30%' }}>Nama</TableTh>
<TableTh style={{ width: '45%' }}>Deskripsi</TableTh>
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
<TableTh fz="sm" fw={600} ta="left">
Nama
</TableTh>
<TableTh fz="sm" fw={600} ta="left">
Deskripsi
</TableTh>
<TableTh fz="sm" fw={600} ta="left">
Aksi
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.length > 0 ? (
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd style={{ width: '30%' }}>
<Box w={200}>
<Text fw={500} truncate="end" lineClamp={1}>
{item.name}
</Text>
</Box>
<TableTd>
<Text fz="md" fw={500} lh={1.5} truncate="end">
{item.name}
</Text>
</TableTd>
<TableTd style={{ width: '45%' }}>
<Box w={200}>
<Text truncate="end" lineClamp={1} fz="sm" c="dimmed"
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
</Box>
<TableTd>
<Text
fz="sm"
lh={1.5}
dangerouslySetInnerHTML={{ __html: item.deskripsi || '' }}
style={{ wordBreak: 'break-word' }}
/>
</TableTd>
<TableTd style={{ width: '15%' }}>
<TableTd>
<Button
size="xs"
radius="md"
@@ -128,7 +138,9 @@ function ListSuratKeterangan({ search }: { search: string }) {
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() =>
router.push(`/admin/desa/layanan/pelayanan_surat_keterangan/${item.id}`)
router.push(
`/admin/desa/layanan/pelayanan_surat_keterangan/${item.id}`
)
}
>
Detail
@@ -139,8 +151,10 @@ function ListSuratKeterangan({ search }: { search: string }) {
) : (
<TableTr>
<TableTd colSpan={3}>
<Center py={20}>
<Text color="dimmed">Tidak ada data surat keterangan yang cocok</Text>
<Center py="xl">
<Text c="dimmed" fz="sm" ta="center">
Tidak ada data surat keterangan yang cocok
</Text>
</Center>
</TableTd>
</TableTr>
@@ -148,7 +162,67 @@ function ListSuratKeterangan({ 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 p="sm" radius="md">
<Stack gap={4}>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Nama
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.name}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Deskripsi
</Text>
<Box pl={8}>
<Text
fz="sm"
fw={500}
lh={1.4}
dangerouslySetInnerHTML={{ __html: item.deskripsi || '' }}
style={{ wordBreak: 'break-word' }}
/>
</Box>
</Box>
<Box>
<Button
fullWidth
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() =>
router.push(
`/admin/desa/layanan/pelayanan_surat_keterangan/${item.id}`
)
}
>
Detail
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Center py="xl">
<Text c="dimmed" fz="sm" ta="center">
Tidak ada data surat keterangan yang cocok
</Text>
</Center>
)}
</Stack>
</Box>
</Paper>
<Center>
<Pagination
value={page}
@@ -167,4 +241,4 @@ function ListSuratKeterangan({ search }: { search: string }) {
);
}
export default SuratKeterangan;
export default SuratKeterangan;

View File

@@ -74,13 +74,13 @@ function EditPelayananTelunjukSakti() {
);
const handleResetForm = () => {
setFormData({
name: originalData.name,
deskripsi: originalData.deskripsi,
link: originalData.link,
});
toast.info("Form dikembalikan ke data awal");
};
setFormData({
name: originalData.name,
deskripsi: originalData.deskripsi,
link: originalData.link,
});
toast.info("Form dikembalikan ke data awal");
};
// Submit: update global state hanya saat simpan
const handleSubmit = async () => {
@@ -102,12 +102,12 @@ function EditPelayananTelunjukSakti() {
};
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">
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />
</Button>
<Title order={4} ml="sm" c="dark">
Edit Pelayanan Telunjuk Sakti Desa
</Title>

View File

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

View File

@@ -47,7 +47,7 @@ function CreatePelayananTelunjukDesa() {
};
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">

View File

@@ -1,5 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
'use client';
import colors from '@/con/colors';
import {
@@ -18,7 +18,7 @@ import {
TableThead,
TableTr,
Text,
Title
Title,
} from '@mantine/core';
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -26,9 +26,10 @@ import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import stateLayananDesa from '../../../_state/desa/layananDesa';
import { useDebouncedValue } from '@mantine/hooks';
function PelayananTelunjukSakti() {
const [search, setSearch] = useState("");
const [search, setSearch] = useState('');
return (
<Box>
<HeaderSearch
@@ -46,46 +47,57 @@ function PelayananTelunjukSakti() {
function ListPelayananTelunjukSakti({ search }: { search: string }) {
const telunjukSaktiState = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load } = telunjukSaktiState.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: 'sm', md: 'md' }}>
<Skeleton height={400} radius="md" />
</Stack>
);
}
return (
<Box py={10}>
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
<Box py={{ base: 'sm', md: 'md' }}>
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
<Group justify="space-between" mb="md">
<Title order={4}>Daftar Pelayanan Telunjuk Sakti</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() =>
router.push('/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/create')
}
>
Tambah Baru
</Button>
<Title order={2} lh={1.2}>
Daftar Pelayanan Telunjuk Sakti
</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() =>
router.push('/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/create')
}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover style={{ minWidth: '700px' }}>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover>
<TableThead>
<TableTr>
<TableTh style={{ width: '30%' }}>Nama</TableTh>
<TableTh style={{ width: '40%' }}>Link</TableTh>
<TableTh style={{ width: '30%' }}>Detail</TableTh>
<TableTh fz="sm" fw={600} ta="left" c="gray.8" w="30%">
Nama
</TableTh>
<TableTh fz="sm" fw={600} ta="left" c="gray.8" w="40%">
Link
</TableTh>
<TableTh fz="sm" fw={600} ta="left" c="gray.8" w="30%">
Detail
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
@@ -93,18 +105,19 @@ function ListPelayananTelunjukSakti({ 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="sm" fw={500} lh={1.5} truncate="end">
{item.name}
</Text>
</TableTd>
<TableTd>
<Box w={200}>
<a href={item.link} target="_blank" rel="noopener noreferrer">
<Text lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }} style={{wordBreak: "break-word", whiteSpace: "normal"}} truncate="end" fz={"sm"} />
</a>
</Box>
<a href={item.link} target="_blank" rel="noopener noreferrer">
<Text
fz="sm"
lh={1.5}
truncate="end"
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
/>
</a>
</TableTd>
<TableTd>
<Button
@@ -117,7 +130,9 @@ function ListPelayananTelunjukSakti({ search }: { search: string }) {
}
>
<IconDeviceImacCog size={20} />
<Text ml={5}>Detail</Text>
<Text ml="xs" fz="sm" fw={500}>
Detail
</Text>
</Button>
</TableTd>
</TableTr>
@@ -125,8 +140,8 @@ function ListPelayananTelunjukSakti({ search }: { search: string }) {
) : (
<TableTr>
<TableTd colSpan={3}>
<Center py={20}>
<Text color="dimmed">
<Center py="lg">
<Text c="dimmed" fz="sm" lh={1.5}>
Tidak ada data layanan yang cocok
</Text>
</Center>
@@ -136,17 +151,68 @@ function ListPelayananTelunjukSakti({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Cards */}
<Box hiddenFrom="md">
{filteredData.length > 0 ? (
<Stack gap="md">
{filteredData.map((item) => (
<Paper key={item.id} withBorder p="md" radius="sm">
<Box mb="xs">
<Text fz='sm' fw={600} lh={1.4} c="gray.8">
Nama
</Text>
<Text fz="sm" fw={500} lh={1.5}>
{item.name}
</Text>
</Box>
<Box mb="xs">
<Text fz='sm' fw={600} lh={1.4} c="gray.8">
Link
</Text>
<Text fz="sm" fw={500} lh={1.5} component="a" href={item.link} target="_blank" rel="noopener noreferrer">
{item.deskripsi}
</Text>
</Box>
<Button
variant="light"
color="blue"
fullWidth
mt="sm"
onClick={() =>
router.push(
`/admin/desa/layanan/pelayanan_telunjuk_sakti_desa/${item.id}`
)
}
>
<IconDeviceImacCog size={20} />
<Text ml="xs" fz="sm" fw={500}>
Detail
</Text>
</Button>
</Paper>
))}
</Stack>
) : (
<Center py="lg">
<Text c="dimmed" fz="sm" lh={1.5}>
Tidak ada data layanan yang cocok
</Text>
</Center>
)}
</Box>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => {
load(newPage, 10, search);
window.scrollTo({ top: 0, behavior: 'smooth' });
}}
}
}
total={totalPages}
mt="md"
mb="md"
color="blue"
radius="md"
/>
@@ -155,5 +221,4 @@ function ListPelayananTelunjukSakti({ search }: { search: string }) {
);
}
export default PelayananTelunjukSakti;
export default PelayananTelunjukSakti;

View File

@@ -133,7 +133,7 @@ function EditPenghargaan() {
};
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">

View File

@@ -49,7 +49,7 @@ function DetailPenghargaan() {
const data = statePenghargaan.findUnique.data;
return (
<Box py={10}>
<Box px={{ base: 0, md: 'xs' }} py="xs">
<Button
variant="subtle"
onClick={() => router.back()}

View File

@@ -73,7 +73,7 @@ function CreatePenghargaan() {
};
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">

View File

@@ -25,6 +25,7 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../_com/header';
import { useDebouncedValue } from '@mantine/hooks';
function Penghargaan() {
const [search, setSearch] = useState("");
@@ -45,45 +46,49 @@ function Penghargaan() {
function ListPenghargaan({ search }: { search: string }) {
const state = useProxy(penghargaanState);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load } = state.findMany;
useEffect(() => {
load(page, 10, search);
}, [page, search]);
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
const filteredData = data || []
const filteredData = data || [];
// Loading state
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton height={600} radius="md" />
<Stack py="md">
<Skeleton h={600} radius="md" />
</Stack>
);
}
return (
<Box py={10}>
<Box py="md">
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
<Group justify="space-between" mb="md">
<Title order={4}>List Penghargaan</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/penghargaan/create')}
>
Tambah Baru
</Button>
<Group justify="space-between" mb="lg">
<Title order={2} visibleFrom="md">List Penghargaan</Title>
<Title order={3} hiddenFrom="md">List Penghargaan</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/penghargaan/create')}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: 'auto' }}>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover>
<TableThead>
<TableTr>
<TableTh style={{ width: '35%' }}>Nama</TableTh>
<TableTh style={{ width: '35%' }}>Deskripsi</TableTh>
<TableTh style={{ width: '30%' }}>Aksi</TableTh>
<TableTh w="35%">Nama</TableTh>
<TableTh w="35%">Deskripsi</TableTh>
<TableTh w="30%">Aksi</TableTh>
</TableTr>
</TableThead>
<TableTbody>
@@ -91,31 +96,27 @@ function ListPenghargaan({ 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.45} truncate="end" lineClamp={1}>
{item.name}
</Text>
</TableTd>
<TableTd>
<Box w={200}>
<Text
truncate="end"
lineClamp={1}
fz="sm"
c="dimmed"
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
style={{wordBreak: "break-word", whiteSpace: "normal"}}
/>
</Box>
<Text
fz="sm"
lh={1.45}
c="dimmed"
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
lineClamp={1}
/>
</TableTd>
<TableTd>
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() =>
router.push(`/admin/desa/penghargaan/${item.id}`)
}
@@ -127,9 +128,9 @@ function ListPenghargaan({ search }: { search: string }) {
))
) : (
<TableTr>
<TableTd colSpan={4}>
<Center py={20}>
<Text color="dimmed">
<TableTd colSpan={3}>
<Center py="xl">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data penghargaan yang cocok
</Text>
</Center>
@@ -139,7 +140,54 @@ function ListPenghargaan({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Cards */}
<Stack gap="sm" hiddenFrom="md">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="md" radius="md">
<Box>
<Text fz="xs" fw={600} lh={1.4}>
Nama
</Text>
<Text fz="sm" fw={500} lh={1.45}>
{item.name}
</Text>
</Box>
<Box mt="xs">
<Text fz="xs" fw={600} lh={1.4}>
Deskripsi
</Text>
<Text lineClamp={3} fz="sm" fw={500} lh={1.45} c="dimmed">
<span dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Text>
</Box>
<Group mt="md">
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() =>
router.push(`/admin/desa/penghargaan/${item.id}`)
}
>
Detail
</Button>
</Group>
</Paper>
))
) : (
<Center py="xl">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data penghargaan yang cocok
</Text>
</Center>
)}
</Stack>
</Paper>
<Center>
<Pagination
value={page}
@@ -148,7 +196,7 @@ function ListPenghargaan({ search }: { search: string }) {
window.scrollTo({ top: 0, behavior: 'smooth' });
}}
total={totalPages}
mt="md"
mt="lg"
mb="md"
color="blue"
radius="md"
@@ -158,4 +206,4 @@ function ListPenghargaan({ search }: { search: string }) {
);
}
export default Penghargaan;
export default Penghargaan;

View File

@@ -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 { IconCategory, IconListDetails } from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
@@ -54,35 +54,76 @@ function LayoutTabsLayanan({ children }: { children: React.ReactNode }) {
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", // ✅ biar nggak nempel ke tepi
}}
<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%"
>
{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>
<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

View File

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

View File

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

View File

@@ -2,11 +2,21 @@
'use client'
import colors from '@/con/colors';
import {
Box, Button, Center,
Box,
Button,
Center,
Pagination,
Paper, Skeleton, Stack,
Table, TableTbody, TableTd, TableTh, TableThead, TableTr,
Text, Title
Paper,
Skeleton,
Stack,
Table,
TableTbody,
TableTd,
TableTh,
TableThead,
TableTr,
Text,
Title,
} from '@mantine/core';
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -15,6 +25,7 @@ import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import stateDesaPengumuman from '../../../_state/desa/pengumuman';
import { useDebouncedValue } from '@mantine/hooks';
function KategoriPengumuman() {
const [search, setSearch] = useState('');
@@ -33,90 +44,134 @@ function KategoriPengumuman() {
}
function ListKategoriPengumuman({ search }: { search: string }) {
const listDataState = useProxy(stateDesaPengumuman.category)
const listDataState = useProxy(stateDesaPengumuman.category);
const router = useRouter();
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null);
const [debouncedSearch] = useDebouncedValue(search, 500);
const { data, page, totalPages, loading, load } = listDataState.findMany;
useEffect(() => {
load(1, 10, search)
}, [search])
load(page, 10, debouncedSearch);
}, [page, debouncedSearch]);
const handleDelete = () => {
if (selectedId) {
listDataState.delete.delete(selectedId)
setModalHapus(false)
setSelectedId(null)
load(page, 10, search)
listDataState.delete.delete(selectedId);
setModalHapus(false);
setSelectedId(null);
load(page, 10, search);
}
}
};
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">
<Stack>
<Box style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 15 }}>
<Title order={4}>List Kategori Pengumuman</Title>
<Box py={{ base: 'md', md: 'lg' }}>
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
<Stack gap={'lg'}>
<Box
visibleFrom="md"
style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
>
<Title order={2} lh={1.1}>
List Kategori Pengumuman
</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/pengumuman/kategori-pengumuman/create')}
onClick={() =>
router.push('/admin/desa/pengumuman/kategori-pengumuman/create')
}
>
Tambah Baru
</Button>
</Box>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover striped withRowBorders style={{ minWidth: '700px' }}>
<Box hiddenFrom="md">
<Stack gap="xs">
<Title order={2} size="md" lh={1.1} ta="left">
List Kategori Pengumuman
</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() =>
router.push('/admin/desa/pengumuman/kategori-pengumuman/create')
}
fullWidth
>
Tambah Baru
</Button>
</Stack>
</Box>
<Box visibleFrom="md">
<Table highlightOnHover striped withRowBorders>
<TableThead>
<TableTr>
<TableTh style={{ width: '10%' }}>No</TableTh>
<TableTh style={{ width: '60%' }}>Nama</TableTh>
<TableTh style={{ width: '15%' }}>Edit</TableTh>
<TableTh style={{ width: '15%' }}>Hapus</TableTh>
<TableTh w="60%">
<Text fz="sm" fw={600} lh={1.4}>
Nama
</Text>
</TableTh>
<TableTh w="15%">
<Text fz="sm" fw={600} lh={1.4}>
Edit
</Text>
</TableTh>
<TableTh w="15%">
<Text fz="sm" fw={600} lh={1.4}>
Hapus
</Text>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.length > 0 ? (
filteredData.map((item, index) => (
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Text fz="sm">{(page - 1) * 10 + index + 1}</Text>
</TableTd>
<TableTd>
<Text truncate lineClamp={1}>{item.name}</Text>
<Text fz="md" fw={500} lh={1.5} truncate>
{item.name}
</Text>
</TableTd>
<TableTd>
<Button
variant='light'
color='green'
onClick={() => router.push(`/admin/desa/pengumuman/kategori-pengumuman/${item.id}`)}
variant="light"
color="green"
onClick={() =>
router.push(
`/admin/desa/pengumuman/kategori-pengumuman/${item.id}`
)
}
size="compact-sm"
>
<IconEdit size={20} />
<IconEdit size={16} />
</Button>
</TableTd>
<TableTd>
<Button
variant='light'
color='red'
variant="light"
color="red"
disabled={listDataState.delete.loading}
onClick={() => {
setSelectedId(item.id)
setModalHapus(true)
}}>
<IconTrash size={20} />
setSelectedId(item.id);
setModalHapus(true);
}}
size="compact-sm"
>
<IconTrash size={16} />
</Button>
</TableTd>
</TableTr>
@@ -124,8 +179,10 @@ function ListKategoriPengumuman({ search }: { search: string }) {
) : (
<TableTr>
<TableTd colSpan={4}>
<Center py={20}>
<Text c="dimmed">Tidak ada data kategori pengumuman yang cocok</Text>
<Center py={24}>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data kategori pengumuman yang cocok
</Text>
</Center>
</TableTd>
</TableTr>
@@ -133,6 +190,71 @@ function ListKategoriPengumuman({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
<Stack hiddenFrom="md" gap="sm">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper
key={item.id}
withBorder
p="md"
radius="md"
bg={colors['white-1']}
>
<Stack gap="xs">
<Box>
<Text fz="xs" fw={600} lh={1.4}>
Nama
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.name}
</Text>
</Box>
<Box
style={{
display: 'flex',
gap: 8,
justifyContent: 'flex-end',
}}
>
<Button
variant="light"
color="green"
onClick={() =>
router.push(
`/admin/desa/pengumuman/kategori-pengumuman/${item.id}`
)
}
size="compact-xs"
>
<IconEdit size={14} />
</Button>
<Button
variant="light"
color="red"
disabled={listDataState.delete.loading}
onClick={() => {
setSelectedId(item.id);
setModalHapus(true);
}}
size="compact-xs"
>
<IconTrash size={14} />
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Paper withBorder p="xl" radius="md" bg={colors['white-1']}>
<Center>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data kategori pengumuman yang cocok
</Text>
</Center>
</Paper>
)}
</Stack>
</Stack>
</Paper>
@@ -153,7 +275,7 @@ function ListKategoriPengumuman({ search }: { search: string }) {
text='Apakah anda yakin ingin menghapus kategori Pengumuman ini?'
/>
</Box>
)
);
}
export default KategoriPengumuman;
export default KategoriPengumuman;

View File

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

View File

@@ -49,7 +49,7 @@ export default function DetailPengumuman() {
const data = pengumumanState.pengumuman.findUnique.data;
return (
<Box py={10}>
<Box px={{ base: 0, md: 'xs' }} py="xs">
<Button
variant="subtle"
onClick={() => router.back()}
@@ -61,7 +61,7 @@ export default function DetailPengumuman() {
<Paper
withBorder
w={{ base: '100%', md: '60%' }}
w={{ base: '100%', md: '70%' }}
bg={colors['white-1']}
p="lg"
radius="md"
@@ -74,14 +74,6 @@ export default function DetailPengumuman() {
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
<Stack gap="sm">
<Box>
<Text fz="lg" fw="bold">
Kategori
</Text>
<Text fz="md" c="dimmed">
{data?.CategoryPengumuman?.name || '-'}
</Text>
</Box>
<Box>
<Text fz="lg" fw="bold">
@@ -92,6 +84,15 @@ export default function DetailPengumuman() {
</Text>
</Box>
<Box>
<Text fz="lg" fw="bold">
Kategori
</Text>
<Text fz="md" c="dimmed">
{data?.CategoryPengumuman?.name || '-'}
</Text>
</Box>
<Box>
<Text fz="lg" fw="bold">
Deskripsi

View File

@@ -55,7 +55,7 @@ function CreatePengumuman() {
};
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">

View File

@@ -19,7 +19,7 @@ import {
Text,
Title
} from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -46,44 +46,56 @@ function Pengumuman() {
function ListPengumuman({ search }: { search: string }) {
const pengumumanState = useProxy(stateDesaPengumuman);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const { data, page, totalPages, loading, load } = pengumumanState.pengumuman.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">
<Group justify="space-between" mb="md">
<Title order={4}>Daftar Pengumuman</Title>
<Box py={{ base: 'sm', md: 'md' }}>
<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={2} lh={1.2}>
Daftar Pengumuman
</Title>
<Button
leftSection={<IconCircleDashedPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/pengumuman/list-pengumuman/create')}
fz={{ base: 'sm', md: 'md' }}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover style={{ minWidth: '700px' }}>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover>
<TableThead>
<TableTr>
<TableTh style={{ width: '40%' }}>Judul</TableTh>
<TableTh style={{ width: '30%' }}>Kategori</TableTh>
<TableTh style={{ width: '20%' }}>Detail</TableTh>
<TableTh fz="sm" fw={600} ta="left">
Judul
</TableTh>
<TableTh fz="sm" fw={600} ta="left">
Kategori
</TableTh>
<TableTh fz="sm" fw={600} ta="left">
Detail
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
@@ -91,14 +103,12 @@ function ListPengumuman({ search }: { search: string }) {
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={150}>
<Text fw={500} truncate="end" lineClamp={1}>
{item.judul}
</Text>
</Box>
<Text fz="md" fw={500} lh={1.45} truncate="end" lineClamp={1}>
{item.judul}
</Text>
</TableTd>
<TableTd>
<Text fz="sm" c="dimmed">
<Text fz="sm" fw={500} lh={1.45} c="dimmed">
{item.CategoryPengumuman?.name || '-'}
</Text>
</TableTd>
@@ -109,9 +119,12 @@ function ListPengumuman({ search }: { search: string }) {
onClick={() =>
router.push(`/admin/desa/pengumuman/list-pengumuman/${item.id}`)
}
fz="sm"
px="sm"
py="xs"
>
<IconDeviceImacCog size={20} />
<Text ml={5}>Detail</Text>
<IconDeviceImacCog size={18} />
<Text ml="xs">Detail</Text>
</Button>
</TableTd>
</TableTr>
@@ -119,8 +132,10 @@ function ListPengumuman({ search }: { search: string }) {
) : (
<TableTr>
<TableTd colSpan={3}>
<Center py={20}>
<Text color="dimmed">Tidak ada pengumuman yang cocok</Text>
<Center py={{ base: 'sm', md: 'md' }}>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada pengumuman yang cocok
</Text>
</Center>
</TableTd>
</TableTr>
@@ -128,7 +143,59 @@ function ListPengumuman({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Card List */}
<Box hiddenFrom="md">
<Stack gap="xs">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="md" radius="sm">
<Stack gap="xs">
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Judul
</Text>
<Text fz="sm" fw={500} lh={1.45}>
{item.judul}
</Text>
</Box>
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Kategori
</Text>
<Text fz="sm" fw={500} lh={1.45} c="dimmed">
{item.CategoryPengumuman?.name || '-'}
</Text>
</Box>
<Box>
<Button
variant="light"
color="blue"
onClick={() =>
router.push(`/admin/desa/pengumuman/list-pengumuman/${item.id}`)
}
fullWidth
fz="sm"
mt="xs"
>
<IconDeviceImacCog size={18} />
<Text ml="xs">Detail</Text>
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Center py="sm">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada pengumuman yang cocok
</Text>
</Center>
)}
</Stack>
</Box>
</Paper>
<Center>
<Pagination
value={page}
@@ -147,4 +214,4 @@ function ListPengumuman({ search }: { search: string }) {
);
}
export default Pengumuman;
export default Pengumuman;

View File

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

View File

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

View File

@@ -1,7 +1,24 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
'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 {
Box,
Button,
Center,
Group,
Pagination,
Paper,
Skeleton,
Stack,
Table,
TableTbody,
TableTd,
TableTh,
TableThead,
TableTr,
Text,
Title,
} from '@mantine/core';
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
@@ -9,6 +26,7 @@ import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import potensiDesaState from '../../../_state/desa/potensi';
import { useDebouncedValue } from '@mantine/hooks';
function KategoriPotensi() {
const [search, setSearch] = useState('');
@@ -27,85 +45,110 @@ function KategoriPotensi() {
}
function ListKategoriPotensi({ search }: { search: string }) {
const listDataState = useProxy(potensiDesaState.kategoriPotensi)
const listDataState = useProxy(potensiDesaState.kategoriPotensi);
const router = useRouter();
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null);
const { data, page, totalPages, loading, load } = listDataState.findMany;
const [debouncedSearch] = useDebouncedValue(search, 1000);
useEffect(() => {
load(1, 10, search)
}, [search])
load(1, 10, debouncedSearch);
}, [debouncedSearch]);
const handleDelete = () => {
if (selectedId) {
listDataState.delete.delete(selectedId)
setModalHapus(false)
setSelectedId(null)
load(page, 10, search)
listDataState.delete.delete(selectedId);
setModalHapus(false);
setSelectedId(null);
load(page, 10, search);
}
}
};
const filteredData = data || []
const filteredData = data || [];
if (loading || !data) {
return (
<Stack py={10}>
<Stack py="md">
<Skeleton height={500} radius="md" />
</Stack>
)
);
}
return (
<Box py={10}>
<Box py="md">
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
<Stack>
<Group justify="space-between">
<Title order={4}>List Kategori Potensi</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/potensi/kategori-potensi/create')}
>
Tambah Baru
</Button>
<Stack gap="xl">
<Group justify="space-between" align="center">
<Title order={2} lh={1.2}>
List Kategori Potensi
</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() =>
router.push('/admin/desa/potensi/kategori-potensi/create')
}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: 'auto' }}>
<Table highlightOnHover striped withRowBorders style={{ minWidth: '700px' }}>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover striped withRowBorders miw={700}>
<TableThead>
<TableTr>
<TableTh style={{ width: '10%' }}>No</TableTh>
<TableTh style={{ width: '60%' }}>Nama</TableTh>
<TableTh style={{ width: '15%' }}>Edit</TableTh>
<TableTh style={{ width: '15%' }}>Hapus</TableTh>
<TableTh w="60%">
<Text fz="xs" fw={600} lh={1.4} c="black">
Nama
</Text>
</TableTh>
<TableTh w="15%">
<Text fz="xs" fw={600} lh={1.4} c="black">
Edit
</Text>
</TableTh>
<TableTh w="15%">
<Text fz="xs" fw={600} lh={1.4} c="black">
Hapus
</Text>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.length > 0 ? (
filteredData.map((item, index) => (
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Text fz="sm">{(page - 1) * 10 + index + 1}</Text>
<Text fz="sm" lh={1.5} truncate>
{item.nama}
</Text>
</TableTd>
<TableTd>
<Text truncate lineClamp={1}>{item.nama}</Text>
</TableTd>
<TableTd>
<Button variant='light' color='green' onClick={() => router.push(`/admin/desa/potensi/kategori-potensi/${item.id}`)}>
<Button
variant="light"
color="green"
onClick={() =>
router.push(
`/admin/desa/potensi/kategori-potensi/${item.id}`
)
}
>
<IconEdit size={20} />
</Button>
</TableTd>
<TableTd>
<Button
variant='light'
color='red'
variant="light"
color="red"
disabled={listDataState.delete.loading}
onClick={() => {
setSelectedId(item.id)
setModalHapus(true)
}}>
setSelectedId(item.id);
setModalHapus(true);
}}
>
<IconTrash size={20} />
</Button>
</TableTd>
@@ -114,8 +157,10 @@ function ListKategoriPotensi({ search }: { search: string }) {
) : (
<TableTr>
<TableTd colSpan={4}>
<Center py={20}>
<Text color="dimmed">Tidak ada data kategori potensi yang cocok</Text>
<Center py="lg">
<Text c="dimmed" fz="sm" lh={1.5}>
Tidak ada data kategori potensi yang cocok
</Text>
</Center>
</TableTd>
</TableTr>
@@ -123,10 +168,70 @@ function ListKategoriPotensi({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Cards */}
<Stack gap="sm" hiddenFrom="md">
{filteredData.length > 0 ? (
filteredData.map((item, index) => (
<Paper key={item.id} withBorder p="sm" radius="md">
<Stack gap={4}>
<Box>
<Text fz="xs" fw={600} lh={1.4}>
No
</Text>
<Text fz="sm" lh={1.5}>
{(page - 1) * 10 + index + 1}
</Text>
</Box>
<Box>
<Text fz="xs" fw={600} lh={1.4}>
Nama
</Text>
<Text fz="sm" lh={1.5}>
{item.nama}
</Text>
</Box>
<Group justify="flex-end" mt="xs">
<Button
variant="light"
color="green"
size="xs"
onClick={() =>
router.push(
`/admin/desa/potensi/kategori-potensi/${item.id}`
)
}
>
<IconEdit size={16} />
</Button>
<Button
variant="light"
color="red"
size="xs"
disabled={listDataState.delete.loading}
onClick={() => {
setSelectedId(item.id);
setModalHapus(true);
}}
>
<IconTrash size={16} />
</Button>
</Group>
</Stack>
</Paper>
))
) : (
<Center py="lg">
<Text c="dimmed" fz="sm" lh={1.5}>
Tidak ada data kategori potensi yang cocok
</Text>
</Center>
)}
</Stack>
</Stack>
</Paper>
<Center mt="md">
<Center mt="xl">
<Pagination
value={page}
onChange={(newPage) => load(newPage, 10, search)}
@@ -143,7 +248,7 @@ function ListKategoriPotensi({ search }: { search: string }) {
text='Apakah anda yakin ingin menghapus kategori Potensi ini?'
/>
</Box>
)
);
}
export default KategoriPotensi;
export default KategoriPotensi;

View File

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

View File

@@ -40,7 +40,7 @@ export default function DetailPotensi() {
const data = potensiState.findUnique.data;
return (
<Box py={10}>
<Box px={{ base: 0, md: 'xs' }} py="xs">
<Button
variant="subtle"
onClick={() => router.back()}
@@ -52,7 +52,7 @@ export default function DetailPotensi() {
<Paper
withBorder
w={{ base: "100%", md: "60%" }}
w={{ base: "100%", md: "70%" }}
bg={colors['white-1']}
p="lg"
radius="md"

View File

@@ -79,7 +79,7 @@ function CreatePotensi() {
};
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">

View File

@@ -26,6 +26,7 @@ import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import potensiDesaState from '../../../_state/desa/potensi';
import { useDebouncedValue } from '@mantine/hooks';
function Potensi() {
const [search, setSearch] = useState("");
@@ -46,6 +47,7 @@ function Potensi() {
function ListPotensi({ search }: { search: string }) {
const potensiState = useProxy(potensiDesaState);
const router = useRouter();
const [debouncedSearch] = useDebouncedValue(search, 1000);
const {
data,
@@ -57,41 +59,61 @@ function ListPotensi({ search }: { search: string }) {
useEffect(() => {
potensiState.kategoriPotensi.findMany.load();
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="lg">
<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 Potensi Desa</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/potensi/list-potensi/create')}
>
Tambah Baru
</Button>
<Box py="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={2} lh={1.2}>
Daftar Potensi Desa
</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/potensi/list-potensi/create')}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: "auto" }}>
<Table highlightOnHover style={{ minWidth: '700px' }}>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover miw={700}>
<TableThead>
<TableTr>
<TableTh style={{ width: '20%' }}>Judul</TableTh>
<TableTh style={{ width: '20%' }}>Kategori</TableTh>
<TableTh style={{ width: '35%' }}>Deskripsi</TableTh>
<TableTh style={{ width: '15%' }}>Detail</TableTh>
<TableTh w="20%">
<Text fz="sm" fw={600} lh={1.4}>
Judul
</Text>
</TableTh>
<TableTh w="20%">
<Text fz="sm" fw={600} lh={1.4}>
Kategori
</Text>
</TableTh>
<TableTh w="35%">
<Text fz="sm" fw={600} lh={1.4}>
Deskripsi
</Text>
</TableTh>
<TableTh w="15%">
<Text fz="sm" fw={600} lh={1.4}>
Detail
</Text>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
@@ -99,27 +121,23 @@ function ListPotensi({ 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} lineClamp={1}>
{item.name}
</Text>
</TableTd>
<TableTd>
<Box w={200}>
<Text fz="sm" c="dimmed">{item.kategori?.nama || '-'}</Text>
</Box>
<Text fz="sm" c="gray.7" lh={1.5}>
{item.kategori?.nama || '-'}
</Text>
</TableTd>
<TableTd>
<Box w={300}>
<Text
lineClamp={1}
truncate
fz="sm"
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
</Box>
<Text
fz="sm"
lh={1.5}
lineClamp={2}
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
style={{ wordBreak: 'break-word' }}
/>
</TableTd>
<TableTd>
<Button
@@ -138,8 +156,10 @@ function ListPotensi({ search }: { search: string }) {
) : (
<TableTr>
<TableTd colSpan={4}>
<Center py={20}>
<Text color="dimmed">Tidak ada data potensi yang cocok</Text>
<Center py="xl">
<Text c="gray.6" fz="sm" ta="center" lh={1.5}>
Tidak ada data potensi yang cocok
</Text>
</Center>
</TableTd>
</TableTr>
@@ -147,7 +167,64 @@ function ListPotensi({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Cards */}
<Box hiddenFrom="md">
{filteredData.length > 0 ? (
<Stack gap="sm">
{filteredData.map((item) => (
<Paper key={item.id} withBorder p="sm" radius="md">
<Box mb="xs">
<Text fz="xs" fw={600} lh={1.4}>
Judul
</Text>
<Text fz="sm" fw={500} lh={1.5}>
{item.name}
</Text>
</Box>
<Box mb="xs">
<Text fz="xs" fw={600} lh={1.4}>
Kategori
</Text>
<Text fz="sm" c="gray.7" lh={1.5}>
{item.kategori?.nama || '-'}
</Text>
</Box>
<Box mb="xs">
<Text fz="xs" fw={600} lh={1.4}>
Deskripsi
</Text>
<Text
fz="sm"
lh={1.5}
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
style={{ wordBreak: 'break-word' }}
/>
</Box>
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() => router.push(`/admin/desa/potensi/list-potensi/${item.id}`)}
w="100%"
>
Detail
</Button>
</Paper>
))}
</Stack>
) : (
<Center py="xl">
<Text c="gray.6" fz="sm" ta="center" lh={1.5}>
Tidak ada data potensi yang cocok
</Text>
</Center>
)}
</Box>
</Paper>
<Center>
<Pagination
value={page}
@@ -166,4 +243,4 @@ function ListPotensi({ search }: { search: string }) {
);
}
export default Potensi;
export default Potensi;

View File

@@ -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 { IconCalendar, IconUser, IconUsers } from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
@@ -60,35 +60,76 @@ function LayoutTabsDetail({ children }: { children: React.ReactNode }) {
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", // ✅ biar nggak nempel ke tepi
}}
<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%"
>
{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>
<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

View File

@@ -148,7 +148,7 @@ function Page() {
// ❌ Error
if (loadError) {
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Stack gap="md">
<Button variant="subtle" onClick={handleBack} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />
@@ -166,7 +166,7 @@ function Page() {
// 🧱 UI utama
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Stack gap="md">
{/* Header */}
<Group mb="sm">

View File

@@ -170,7 +170,7 @@ function Page() {
// Loading state
if (maskotState.findUnique.loading || maskotState.update.loading) {
return (
<Box>
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Center h={400}>
<Text>Memuat data...</Text>
</Center>
@@ -181,7 +181,7 @@ function Page() {
// Error state
if (maskotState.findUnique.error) {
return (
<Box>
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Stack gap="md">
<Button variant="subtle" onClick={handleBack}>
<IconArrowBack color={colors['blue-button']} size={20} />
@@ -196,7 +196,7 @@ function Page() {
}
return (
<Box>
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Stack gap="xs">
<Group mb="md">
<Button variant="subtle" onClick={handleBack} p="xs" radius="md">

View File

@@ -91,7 +91,7 @@ function Page() {
}, [params?.id, router]);
// 🔄 Check if form has changes
// 🔁 Reset Form to Original Data
const handleResetForm = () => {
@@ -149,7 +149,7 @@ function Page() {
// 🔄 Loading State
if (isLoading) {
return (
<Box>
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Center h={400}>
<Stack align="center" gap="md">
<Loader size="lg" color={colors['blue-button']} />
@@ -165,7 +165,7 @@ function Page() {
// ❌ Error State
if (loadError) {
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Stack gap="md">
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />

View File

@@ -126,7 +126,7 @@ function Page() {
// ⏳ Loading
if (isLoading) {
return (
<Box>
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Center h={400}>
<Stack align="center" gap="md">
<Loader size="lg" color={colors['blue-button']} />
@@ -142,7 +142,7 @@ function Page() {
// ❌ Error
if (loadError) {
return (
<Box px={{ base: 'sm', md: 'lg' }} py="md">
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Stack gap="md">
<Button variant="subtle" onClick={handleBack} p="xs" radius="md">
<IconArrowBack color={colors['blue-button']} size={24} />

View File

@@ -1,7 +1,7 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Card, Center, Divider, Grid, GridCol, Image, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
import { Box, Button, Card, Center, Divider, Group, Image, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
import { IconEdit } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
@@ -30,208 +30,558 @@ function Page() {
<Title order={2} c={colors['blue-button']}>Preview Profil Desa</Title>
{/* Sejarah Desa */}
{sejarah && (
<Paper p="xl" bg={'white'} withBorder radius="md" shadow="xs">
<Grid align="center">
<GridCol span={{ base: 12, md: 11 }}>
<Title order={3} c={colors['blue-button']}>Preview Sejarah Desa</Title>
</GridCol>
<GridCol span={{ base: 12, md: 1 }}>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-desa/${sejarah.id}/sejarah_desa`)}
>
Edit
</Button>
</GridCol>
</Grid>
<Box px={{ base: 0, md: 50 }} py="xl">
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
<Stack gap={0}>
<Center>
<Image loading='lazy' src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
</Center>
<Paper
bg={colors['blue-button']}
py="md"
px="sm"
radius="md"
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.2rem", md: "1.6rem" }}>
{sejarah.judul}
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
<Text fz={{ base: "md", md: "h3" }} style={{wordBreak: "break-word", whiteSpace: "normal"}} ta="justify" dangerouslySetInnerHTML={{ __html: sejarah.deskripsi }} />
</Paper>
</Box>
</Paper>
)}
<Box visibleFrom='md'>
{sejarah && (
<Paper p={{ base: "lg", md: "xl" }} bg={'white'} withBorder radius="md" shadow="xs">
<Group justify='space-between'>
<Title order={3} c={colors['blue-button']}>Preview Sejarah Desa</Title>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-desa/${sejarah.id}/sejarah_desa`)}
>
Edit
</Button>
</Group>
<Box py="xl">
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
<Stack gap={0}>
<Center>
<Image loading='lazy' src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
</Center>
<Paper
bg={colors['blue-button']}
py="md"
px="sm"
radius="md"
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.2rem", md: "1.6rem" }}>
{sejarah.judul}
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
<Box px={20}>
<Text fz={{ base: "md", md: "h3" }} style={{ wordBreak: "break-word", whiteSpace: "normal" }} ta="justify" dangerouslySetInnerHTML={{ __html: sejarah.deskripsi }} />
</Box>
</Paper>
</Box>
</Paper>
)}
</Box>
<Box hiddenFrom='md'>
{sejarah && (
<Paper p={{ base: "md", md: "xl" }} bg="white" withBorder radius="md" shadow="xs">
{/* Header */}
<Group justify="space-between" align="center" mb="md">
<Title order={3} c={colors['blue-button']}>
Preview Sejarah Desa
</Title>
<Button
variant="light"
color="green"
radius="md"
leftSection={<IconEdit size={18} />}
onClick={() =>
router.push(`/admin/desa/profil/profil-desa/${sejarah.id}/sejarah_desa`)
}
>
Edit
</Button>
</Group>
{/* Content Wrapper */}
<Box
mx="auto"
w="100%"
maw={720} // batas nyaman baca
>
<Paper
bg={colors['white-1']}
withBorder
radius="md"
p={{ base: "md", md: "lg" }}
>
{/* Logo + Title */}
<Stack align="center" gap="xs">
<Image
src="/darmasaba-icon.png"
alt="Logo Desa"
w={{ base: 120, md: 200 }}
loading="lazy"
/>
<Paper
bg={colors['blue-button']}
px="md"
py="sm"
radius="md"
mt={{ base: "sm", md: -24 }} // aman di mobile
>
<Text
ta="center"
c="white"
fw={700}
fz={{ base: "lg", md: "xl" }}
>
{sejarah.judul}
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
{/* Deskripsi */}
<Box pl={10}>
<Text
fz="sm"
lh="1.6"
ta="left"
style={{ wordBreak: "break-word" }}
dangerouslySetInnerHTML={{ __html: sejarah.deskripsi }}
/>
</Box>
</Paper>
</Box>
</Paper>
)}
</Box>
{/* Visi Misi Desa */}
{visiMisi && (
<Paper p="xl" bg={'white'} withBorder radius="md" shadow="xs">
<Grid align="center">
<GridCol span={{ base: 12, md: 11 }}>
<Box visibleFrom='md'>
{visiMisi && (
<Paper p="xl" bg={'white'} withBorder radius="md" shadow="xs">
<Group justify='space-between'>
<Title order={3} c={colors['blue-button']}>Preview Visi Misi Desa</Title>
</GridCol>
<GridCol span={{ base: 12, md: 1 }}>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-desa/${visiMisi.id}/visi_misi_desa`)}
>
Edit
</Button>
</GridCol>
</Grid>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-desa/${visiMisi.id}/visi_misi_desa`)}
>
Edit
</Button>
</Group>
<Box px={{ base: 0, md: 50 }} py="xl">
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
<Stack gap={0}>
<Center>
<Image loading='lazy' src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
</Center>
<Paper
bg={colors['blue-button']}
py="md"
px="sm"
radius="md"
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.2rem", md: "1.6rem" }}>
Visi Misi Desa
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
<Text fw="bold" fz={{ base: "lg", md: "h2" }}>Visi Desa</Text>
<Text fz={{ base: "md", md: "h3" }} ta="justify" style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: visiMisi.visi }} />
<Text fw="bold" fz={{ base: "lg", md: "h2" }}>Misi Desa</Text>
<Text fz={{ base: "md", md: "h3" }} ta="justify" style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: visiMisi.misi }} />
</Paper>
</Box>
</Paper>
)}
<Box px={{ base: 0, md: 50 }} py="xl">
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
<Stack gap={0}>
<Center>
<Image loading='lazy' src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
</Center>
<Paper
bg={colors['blue-button']}
py="md"
px="sm"
radius="md"
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.2rem", md: "1.6rem" }}>
Visi Misi Desa
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
<Box px={20}>
<Text fw="bold" fz={{ base: "lg", md: "h2" }}>Visi Desa</Text>
<Text fz={{ base: "md", md: "h3" }} ta="justify" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: visiMisi.visi }} />
</Box>
<Box px={20}>
<Text fw="bold" fz={{ base: "lg", md: "h2" }}>Misi Desa</Text>
<Text fz={{ base: "md", md: "h3" }} ta="justify" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: visiMisi.misi }} />
</Box>
</Paper>
</Box>
</Paper>
)}
</Box>
<Box hiddenFrom='md'>
{visiMisi && (
<Paper p={{ base: "md", md: "xl" }} bg="white" withBorder radius="md" shadow="xs">
{/* Header */}
<Group justify="space-between" align="center" mb="md">
<Title order={3} c={colors['blue-button']}>
Preview Visi Misi Desa
</Title>
<Button
variant="light"
color="green"
radius="md"
leftSection={<IconEdit size={18} />}
onClick={() => router.push(`/admin/desa/profil/profil-desa/${visiMisi.id}/visi_misi_desa`)}
>
Edit
</Button>
</Group>
{/* Content Wrapper */}
<Box
mx="auto"
w="100%"
maw={720} // batas nyaman baca
>
<Paper
bg={colors['white-1']}
withBorder
radius="md"
p={{ base: "md", md: "lg" }}
>
{/* Logo + Title */}
<Stack align="center" gap="xs">
<Image
src="/darmasaba-icon.png"
alt="Logo Desa"
w={{ base: 120, md: 200 }}
loading="lazy"
/>
<Paper
bg={colors['blue-button']}
px="md"
py="sm"
radius="md"
mt={{ base: "sm", md: -24 }} // aman di mobile
>
<Text
ta="center"
c="white"
fw={700}
fz={{ base: "lg", md: "xl" }}
>
Visi Misi Desa
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
<Stack pl={10}>
<Text fw="bold" fz={{ base: "lg", md: "h2" }}>Visi Desa</Text>
<Text
fz="sm"
lh="1.6"
ta="left"
style={{ wordBreak: "break-word" }}
dangerouslySetInnerHTML={{ __html: visiMisi.visi }}
/>
</Stack>
<Stack pl={10}>
<Text fw="bold" fz={{ base: "lg", md: "h2" }}>Misi Desa</Text>
<Text
fz="sm"
lh="1.6"
ta="left"
style={{ wordBreak: "break-word" }}
dangerouslySetInnerHTML={{ __html: visiMisi.misi }}
/>
</Stack>
</Paper>
</Box>
</Paper>
)}
</Box>
{/* Lambang Desa */}
{lambang && (
<Paper p="xl" bg={'white'} withBorder radius="md" shadow="xs">
<Grid align="center">
<GridCol span={{ base: 12, md: 11 }}>
<Box visibleFrom='md'>
{lambang && (
<Paper p="xl" bg={'white'} withBorder radius="md" shadow="xs">
<Group justify='space-between'>
<Title order={3} c={colors['blue-button']}>Preview Lambang Desa</Title>
</GridCol>
<GridCol span={{ base: 12, md: 1 }}>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-desa/${lambang.id}/lambang_desa`)}
>
Edit
</Button>
</GridCol>
</Grid>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-desa/${lambang.id}/lambang_desa`)}
>
Edit
</Button>
</Group>
<Box px={{ base: 0, md: 50 }} py="xl">
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
<Stack gap={0}>
<Center>
<Image loading='lazy' src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
</Center>
<Paper
bg={colors['blue-button']}
py="md"
px="sm"
radius="md"
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.2rem", md: "1.6rem" }}>
{lambang.judul}
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
<Text fz={{ base: "md", md: "h3" }} ta="justify" style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: lambang.deskripsi }} />
</Paper>
</Box>
</Paper>
)}
<Box px={{ base: 0, md: 50 }} py="xl">
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
<Stack gap={0}>
<Center>
<Image loading='lazy' src="/darmasaba-icon.png" w={{ base: 150, md: 250 }} alt="Logo Desa" />
</Center>
<Paper
bg={colors['blue-button']}
py="md"
px="sm"
radius="md"
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.2rem", md: "1.6rem" }}>
{lambang.judul}
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
<Box px={20}>
<Text fz={{ base: "md", md: "h3" }} ta="justify" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: lambang.deskripsi }} />
</Box>
</Paper>
</Box>
</Paper>
)}
</Box>
<Box hiddenFrom='md'>
{lambang && (
<Paper p={{ base: "md", md: "xl" }} bg="white" withBorder radius="md" shadow="xs">
{/* Header */}
<Group justify="space-between" align="center" mb="md">
<Title order={3} c={colors['blue-button']}>
Preview Lambang Desa
</Title>
<Button
variant="light"
color="green"
radius="md"
leftSection={<IconEdit size={18} />}
onClick={() =>
router.push(`/admin/desa/profil/profil-desa/${lambang.id}/lambang_desa`)
}
>
Edit
</Button>
</Group>
{/* Content Wrapper */}
<Box
mx="auto"
w="100%"
maw={720} // batas nyaman baca
>
<Paper
bg={colors['white-1']}
withBorder
radius="md"
p={{ base: "md", md: "lg" }}
>
{/* Logo + Title */}
<Stack align="center" gap="xs">
<Image
src="/darmasaba-icon.png"
alt="Logo Desa"
w={{ base: 120, md: 200 }}
loading="lazy"
/>
<Paper
bg={colors['blue-button']}
px="md"
py="sm"
radius="md"
mt={{ base: "sm", md: -24 }} // aman di mobile
>
<Text
ta="center"
c="white"
fw={700}
fz={{ base: "lg", md: "xl" }}
>
{lambang.judul}
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
{/* Deskripsi */}
<Box pl={10}>
<Text
fz="sm"
lh="1.6"
ta="left"
style={{ wordBreak: "break-word" }}
dangerouslySetInnerHTML={{ __html: lambang.deskripsi }}
/>
</Box>
</Paper>
</Box>
</Paper>
)}
</Box>
{/* Maskot Desa */}
{maskot && (
<Paper p="xl" bg={'white'} withBorder radius="md" shadow="xs">
<Grid align="center">
<GridCol span={{ base: 12, md: 11 }}>
<Box visibleFrom='md'>
{maskot && (
<Paper p="xl" bg={'white'} withBorder radius="md" shadow="xs">
<Group justify='space-between'>
<Title order={3} c={colors['blue-button']}>Preview Maskot Desa</Title>
</GridCol>
<GridCol span={{ base: 12, md: 1 }}>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-desa/${maskot.id}/maskot_desa`)}
>
Edit
</Button>
</GridCol>
</Grid>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-desa/${maskot.id}/maskot_desa`)}
>
Edit
</Button>
</Group>
<Box px={{ base: 0, md: 50 }} py="xl">
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
<Stack gap={0}>
<Center>
<Image loading='lazy' src="/pudak-icon.png" w={{ base: 150, md: 250 }} alt="Maskot Desa" />
</Center>
<Paper
bg={colors['blue-button']}
py="md"
px="sm"
radius="md"
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.2rem", md: "1.6rem" }}>
Maskot Desa
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
<Text fz={{ base: "md", md: "h3" }} ta="justify" style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: maskot.deskripsi }} />
<Stack mt="md" gap="sm">
<SimpleGrid cols={{ base: 1, md: 4 }} spacing="md">
{maskot.images.map((img, idx) => (
<Card withBorder key={idx} p="xs" w={{ base: '100%', md: 180 }}>
<Center>
<Image
src={img.image.link}
alt={img.label}
w={150}
h={150}
fit="cover"
radius="md"
style={{ border: '1px solid #ccc' }}
loading='lazy'
/>
</Center>
<Text ta="center" mt="xs" fw="bold">{img.label}</Text>
</Card>
))}
</SimpleGrid>
</Stack>
</Paper>
</Box>
</Paper>
)}
<Box px={{ base: 0, md: 50 }} py="xl">
<Paper bg={colors['white-1']} withBorder radius="md" shadow="xs" p="lg">
<Stack gap={0}>
<Center>
<Image loading='lazy' src="/pudak-icon.png" w={{ base: 150, md: 250 }} alt="Maskot Desa" />
</Center>
<Paper
bg={colors['blue-button']}
py="md"
px="sm"
radius="md"
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.2rem", md: "1.6rem" }}>
Maskot Desa
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
<Box px={20}>
<Text fz={{ base: "md", md: "h3" }} ta="justify" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: maskot.deskripsi }} />
</Box>
<Stack mt="md" gap="sm">
<SimpleGrid cols={{ base: 1, md: 4 }} spacing="md">
{maskot.images.map((img, idx) => (
<Card withBorder key={idx} p="xs" w={{ base: '100%', md: 180 }}>
<Center>
<Image
src={img.image.link}
alt={img.label}
w={150}
h={150}
fit="cover"
radius="md"
style={{ border: '1px solid #ccc' }}
loading='lazy'
/>
</Center>
<Text ta="center" mt="xs" fw="bold">{img.label}</Text>
</Card>
))}
</SimpleGrid>
</Stack>
</Paper>
</Box>
</Paper>
)}
</Box>
<Box hiddenFrom='md'>
{maskot && (
<Paper p={{ base: "md", md: "xl" }} bg="white" withBorder radius="md" shadow="xs">
{/* Header */}
<Group justify="space-between" align="center" mb="md">
<Title order={3} c={colors['blue-button']}>
Preview Maskot Desa
</Title>
<Button
variant="light"
color="green"
radius="md"
leftSection={<IconEdit size={18} />}
onClick={() =>
router.push(`/admin/desa/profil/profil-desa/${maskot.id}/maskot_desa`)
}
>
Edit
</Button>
</Group>
{/* Content Wrapper */}
<Box
mx="auto"
w="100%"
maw={720} // batas nyaman baca
>
<Paper
bg={colors['white-1']}
withBorder
radius="md"
p={{ base: "md", md: "lg" }}
>
{/* Logo + Title */}
<Stack align="center" gap="xs">
<Image
src="/pudak-icon.png"
alt="Logo Desa"
w={{ base: 120, md: 200 }}
loading="lazy"
/>
<Paper
bg={colors['blue-button']}
px="md"
py="sm"
radius="md"
mt={{ base: "sm", md: -24 }} // aman di mobile
>
<Text
ta="center"
c="white"
fw={700}
fz={{ base: "lg", md: "xl" }}
>
Maskot Desa
</Text>
</Paper>
</Stack>
<Divider my="md" color={colors['blue-button']} />
{/* Deskripsi */}
<Box pl={10}>
<Text
fz="sm"
lh="1.6"
ta="left"
style={{ wordBreak: "break-word" }}
dangerouslySetInnerHTML={{ __html: maskot.deskripsi }}
/>
</Box>
<Stack mt="md" gap="sm">
<SimpleGrid cols={{ base: 1, md: 4 }} spacing="md">
{maskot.images.map((img, idx) => (
<Card withBorder key={idx} p="xs" w={{ base: '100%', md: 180 }}>
<Center>
<Image
src={img.image.link}
alt={img.label}
w={150}
h={150}
fit="cover"
radius="md"
style={{ border: '1px solid #ccc' }}
loading='lazy'
/>
</Center>
<Text ta="center" mt="xs" fw="bold">{img.label}</Text>
</Card>
))}
</SimpleGrid>
</Stack>
</Paper>
</Box>
</Paper>
)}
</Box>
</Stack>
</Paper>
);

View File

@@ -127,7 +127,7 @@ function EditPerbekelDariMasaKeMasa() {
};
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} />

View File

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

View File

@@ -56,7 +56,7 @@ function CreatePerbekelDariMasaKeMasa() {
};
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">

View File

@@ -1,7 +1,24 @@
'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 {
Box,
Button,
Center,
Group,
Pagination,
Paper,
Skeleton,
Stack,
Table,
TableTbody,
TableTd,
TableTh,
TableThead,
TableTr,
Text,
Title,
} from '@mantine/core';
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -29,43 +46,47 @@ function ListPerbekelDariMasaKeMasa({ search }: { search: string }) {
const state = useProxy(stateProfileDesa.mantanPerbekel)
const router = useRouter();
const { data, page, totalPages, loading, load } = state.findMany;
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
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={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}>List Perbekel Dari Masa Ke Masa</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/profil/profil-perbekel-dari-masa-ke-masa/create')}
>
Tambah Baru
</Button>
<Box py={{ base: 'sm', md: 'md' }}>
<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={2} lh={1.2}>
List Perbekel Dari Masa Ke Masa
</Title>
<Button
leftSection={<IconPlus size={18} />}
color="blue"
variant="light"
onClick={() => router.push('/admin/desa/profil/profil-perbekel-dari-masa-ke-masa/create')}
>
Tambah Baru
</Button>
</Group>
<Box style={{ overflowX: "auto" }}>
<Table highlightOnHover>
{/* Desktop Table */}
<Box visibleFrom="md">
<Table highlightOnHover miw={0}>
<TableThead>
<TableTr>
<TableTh style={{ width: '35%' }}>Nama Perbekel</TableTh>
<TableTh style={{ width: '35%' }}>Periode</TableTh>
<TableTh style={{ width: '20%' }}>Aksi</TableTh>
<TableTh fz="sm" fw={600} ta="left" c="dark.9">Nama Perbekel</TableTh>
<TableTh fz="sm" fw={600} ta="left" c="dark.9">Periode</TableTh>
<TableTh fz="sm" fw={600} ta="left" c="dark.9">Aksi</TableTh>
</TableTr>
</TableThead>
<TableTbody>
@@ -73,36 +94,32 @@ function ListPerbekelDariMasaKeMasa({ search }: { search: string }) {
filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={200}>
<Text fw={500} lineClamp={1}>{item.nama}</Text>
</Box>
<Text fz="md" fw={500} lh={1.5} lineClamp={1}>{item.nama}</Text>
</TableTd>
<TableTd>
<Box w={200}>
<Text lineClamp={1}>{item.periode}</Text>
</Box>
<Text fz="md" lh={1.5} lineClamp={1}>{item.periode}</Text>
</TableTd>
<TableTd>
<Box w={200}>
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() => router.push(`/admin/desa/profil/profil-perbekel-dari-masa-ke-masa/${item.id}`)}
>
Detail
</Button>
</Box>
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() => router.push(`/admin/desa/profil/profil-perbekel-dari-masa-ke-masa/${item.id}`)}
>
Detail
</Button>
</TableTd>
</TableTr>
))
) : (
<TableTr>
<TableTd colSpan={3}>
<Center py={20}>
<Text color="dimmed">Tidak ada data perbekel yang cocok</Text>
<Center py={{ base: 'md', md: 'lg' }}>
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data perbekel yang cocok
</Text>
</Center>
</TableTd>
</TableTr>
@@ -110,6 +127,47 @@ function ListPerbekelDariMasaKeMasa({ search }: { search: string }) {
</TableTbody>
</Table>
</Box>
{/* Mobile Card View */}
<Box hiddenFrom="md">
<Stack gap="xs">
{filteredData.length > 0 ? (
filteredData.map((item) => (
<Paper key={item.id} withBorder p="sm" radius="md">
<Stack gap={4}>
<Box>
<Text fz="xs" fw={600} lh={1.4} c="dark.9">Nama Perbekel</Text>
<Text fz="sm" fw={500} lh={1.5}>{item.nama}</Text>
</Box>
<Box>
<Text fz="xs" fw={600} lh={1.4} c="dark.9">Periode</Text>
<Text fz="sm" lh={1.5}>{item.periode}</Text>
</Box>
<Box>
<Button
size="xs"
radius="md"
variant="light"
color="blue"
leftSection={<IconDeviceImacCog size={16} />}
onClick={() => router.push(`/admin/desa/profil/profil-perbekel-dari-masa-ke-masa/${item.id}`)}
fullWidth
>
Detail
</Button>
</Box>
</Stack>
</Paper>
))
) : (
<Center py="md">
<Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data perbekel yang cocok
</Text>
</Center>
)}
</Stack>
</Box>
</Paper>
<Center>
@@ -120,8 +178,8 @@ function ListPerbekelDariMasaKeMasa({ 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"
/>
@@ -130,4 +188,4 @@ function ListPerbekelDariMasaKeMasa({ search }: { search: string }) {
);
}
export default PerbekelDariMasaKeMasa;
export default PerbekelDariMasaKeMasa;

View File

@@ -97,7 +97,7 @@ function ProfilePerbekel() {
}
return (
<Box py={10}>
<Box px={{ base: 0, md: 'lg' }} py="xs">
<Stack gap="xs">
{/* Header */}
<Group mb="md">

View File

@@ -33,18 +33,18 @@ function Page() {
{/* Header + tombol edit */}
<Grid align="center">
<GridCol span={{ base: 12, md: 11 }}>
<Title order={3} c={colors['blue-button']}>Preview Profil PPID</Title>
<Title order={2} c={colors['blue-button']} lh={1.2} />
</GridCol>
<GridCol span={{ base: 12, md: 1 }}>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-perbekel/${perbekel.id}`)}
>
Edit
</Button>
<Button
c="green"
variant="light"
leftSection={<IconEdit size={18} stroke={2} />}
radius="md"
onClick={() => router.push(`/admin/desa/profil/profil-perbekel/${perbekel.id}`)}
>
Edit
</Button>
</GridCol>
</Grid>
@@ -58,7 +58,13 @@ function Page() {
</Center>
</GridCol>
<GridCol span={12}>
<Text ta="center" fz={{ base: "1.2rem", md: "1.8rem" }} fw="bold" c={colors['blue-button']}>
<Text
ta="center"
fz={{ base: 'sm', md: 'md' }}
fw="bold"
c={colors['blue-button']}
lh={{ base: 1.45, md: 1.45 }}
>
Profil Pimpinan Badan Publik Desa Darmasaba
</Text>
</GridCol>
@@ -86,25 +92,55 @@ function Page() {
className="glass3"
style={{ mt: -30, boxShadow: '0 4px 20px rgba(0,0,0,0.15)' }}
>
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.2rem", md: "1.6rem" }}>
I.B. Surya Prabhawa Manuaba, S.H., M.H.
<Text
ta="center"
c={colors['white-1']}
fw="bolder"
fz={{ base: 'md', md: 'lg' }}
lh={{ base: 1.4, md: 1.4 }}
>
I.B. Surya Prabhawa Manuaba, S.H., M.H.
</Text>
</Paper>
</Stack>
{/* Biodata & Info */}
<Box mt="lg">
<Text fz={{ base: "1.125rem", md: "1.5rem" }} fw="bold" mb={4}>Biodata</Text>
<Text fz={{ base: "1rem", md: "1.4rem" }} ta="justify" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: perbekel.biodata }} />
<Title order={3} lh={1.2} mb={4} />
<Text
fz={{ base: 'sm', md: 'md' }}
lh={{ base: 1.6, md: 1.6 }}
ta="justify"
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
dangerouslySetInnerHTML={{ __html: perbekel.biodata }}
/>
<Text fz={{ base: "1.125rem", md: "1.5rem" }} fw="bold" mt="md" mb={4}>Pengalaman</Text>
<Text fz={{ base: "1rem", md: "1.4rem" }} ta="justify" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: perbekel.pengalaman }} />
<Title order={3} lh={1.2} mt="md" mb={4} />
<Text
fz={{ base: 'sm', md: 'md' }}
lh={{ base: 1.6, md: 1.6 }}
ta="justify"
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
dangerouslySetInnerHTML={{ __html: perbekel.pengalaman }}
/>
<Text fz={{ base: "1.125rem", md: "1.5rem" }} fw="bold" mt="md" mb={4}>Pengalaman Organisasi</Text>
<Text fz={{ base: "1rem", md: "1.4rem" }} ta="justify" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: perbekel.pengalamanOrganisasi }} />
<Title order={3} lh={1.2} mt="md" mb={4} />
<Text
fz={{ base: 'sm', md: 'md' }}
lh={{ base: 1.6, md: 1.6 }}
ta="justify"
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
dangerouslySetInnerHTML={{ __html: perbekel.pengalamanOrganisasi }}
/>
<Text fz={{ base: "1.125rem", md: "1.5rem" }} fw="bold" mt="md" mb={4}>Program Kerja Unggulan</Text>
<Text fz={{ base: "1rem", md: "1.4rem" }} ta="justify" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: perbekel.programUnggulan }} />
<Title order={3} lh={1.2} mt="md" mb={4} />
<Text
fz={{ base: 'sm', md: 'md' }}
lh={{ base: 1.6, md: 1.6 }}
ta="justify"
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
dangerouslySetInnerHTML={{ __html: perbekel.programUnggulan }}
/>
</Box>
</Paper>
</Stack>
@@ -112,4 +148,4 @@ function Page() {
);
}
export default Page;
export default Page;