Fix QC Kak Inno Mobile Done
FIx QC Kak Ayu Mobile Admin Done Fix Tampilan Admin Mobile Device All Menu Done
This commit is contained in:
@@ -68,7 +68,7 @@ const category = proxy({
|
|||||||
const res = await ApiFetch.api.desa.kategoripengumuman[
|
const res = await ApiFetch.api.desa.kategoripengumuman[
|
||||||
"findMany"
|
"findMany"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query: { page, limit, search },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const potensiDesa = proxy({
|
|||||||
const res = await ApiFetch.api.desa.potensi[
|
const res = await ApiFetch.api.desa.potensi[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get({
|
].get({
|
||||||
query: { page, limit },
|
query: { page, limit, search },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Center, Divider, Grid, GridCol, Image, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Center, Divider, Grid, GridCol, Group, Image, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
@@ -31,11 +31,8 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Header + tombol edit */}
|
{/* Header + tombol edit */}
|
||||||
<Grid align="center">
|
<Group justify="space-between">
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
<Title order={2} c={colors['blue-button']} lh={1.2}>Profil Perbekel</Title>
|
||||||
<Title order={2} c={colors['blue-button']} lh={1.2} />
|
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }}>
|
|
||||||
<Button
|
<Button
|
||||||
c="green"
|
c="green"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -45,8 +42,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Card Profil */}
|
{/* Card Profil */}
|
||||||
<Paper p="xl" bg={colors['white-1']} withBorder radius="md" shadow="xs">
|
<Paper p="xl" bg={colors['white-1']} withBorder radius="md" shadow="xs">
|
||||||
@@ -60,7 +56,7 @@ function Page() {
|
|||||||
<GridCol span={12}>
|
<GridCol span={12}>
|
||||||
<Text
|
<Text
|
||||||
ta="center"
|
ta="center"
|
||||||
fz={{ base: 'sm', md: 'md' }}
|
fz={{ base: 'sm', md: 'xl' }}
|
||||||
fw="bold"
|
fw="bold"
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
lh={{ base: 1.45, md: 1.45 }}
|
lh={{ base: 1.45, md: 1.45 }}
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
|
|||||||
<Text fz={"sm"} fw={600} lh={1.4}>
|
<Text fz={"sm"} fw={600} lh={1.4}>
|
||||||
Deskripsi Singkat
|
Deskripsi Singkat
|
||||||
</Text>
|
</Text>
|
||||||
|
<Box pl={5}>
|
||||||
<Text
|
<Text
|
||||||
fz="sm"
|
fz="sm"
|
||||||
fw={500}
|
fw={500}
|
||||||
@@ -192,6 +193,7 @@ function ListInfoTeknologiTepatGuna({ search }: { search: string }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
<Group justify="flex-end">
|
<Group justify="flex-end">
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Title,
|
Title,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -50,6 +50,7 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { data, page, totalPages, loading, load } = stateKategori.findMany;
|
const { data, page, totalPages, loading, load } = stateKategori.findMany;
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
|
||||||
|
|
||||||
const handleHapus = () => {
|
const handleHapus = () => {
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
@@ -60,8 +61,8 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10, search);
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search]);
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || [];
|
const filteredData = data || [];
|
||||||
|
|
||||||
@@ -211,7 +212,7 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
<Box hiddenFrom="md">{renderMobileCards()}</Box>
|
<Box hiddenFrom="md">{renderMobileCards()}</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{totalPages > 1 && (
|
|
||||||
<Center mt="xl">
|
<Center mt="xl">
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
@@ -224,7 +225,6 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
radius="md"
|
radius="md"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
)}
|
|
||||||
|
|
||||||
<ModalKonfirmasiHapus
|
<ModalKonfirmasiHapus
|
||||||
opened={modalHapus}
|
opened={modalHapus}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
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 { useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -29,10 +29,11 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const listState = useProxy(korupsiState.desaAntikorupsi);
|
const listState = useProxy(korupsiState.desaAntikorupsi);
|
||||||
const { data, page, totalPages, loading, load } = listState.findMany;
|
const { data, page, totalPages, loading, load } = listState.findMany;
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10, search);
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search]);
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || [];
|
const filteredData = data || [];
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ function ListResponden({ search }: ListRespondenProps) {
|
|||||||
const { data, page, totalPages, loading, load } = state.findMany;
|
const { data, page, totalPages, loading, load } = state.findMany;
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10);
|
load(page, 10,);
|
||||||
}, [page]);
|
}, [page]);
|
||||||
|
|
||||||
const filteredData = (data || []).filter((item) => {
|
const filteredData = (data || []).filter((item) => {
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ function ListMediaSosial({ search }: { search: string }) {
|
|||||||
{filteredData.length > 0 ? (
|
{filteredData.length > 0 ? (
|
||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<Paper key={item.id} withBorder p="sm" radius="md">
|
<Paper key={item.id} withBorder p="sm" radius="md">
|
||||||
|
<Stack gap={"xs"}>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="sm" fw={600} lh={1.4}>Nama Media Sosial / Kontak</Text>
|
<Text fz="sm" fw={600} lh={1.4}>Nama Media Sosial / Kontak</Text>
|
||||||
<Text fw={500} fz="sm" lh={1.45}>
|
<Text fw={500} fz="sm" lh={1.45}>
|
||||||
@@ -252,6 +253,7 @@ function ListMediaSosial({ search }: { search: string }) {
|
|||||||
Detail
|
Detail
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import profileLandingPageState from '@/app/admin/(dashboard)/_state/landing-page/profile';
|
import profileLandingPageState from '@/app/admin/(dashboard)/_state/landing-page/profile';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Center, Divider, Grid, GridCol, Image, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Center, Divider, Grid, GridCol, Group, Image, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
@@ -29,13 +29,10 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Grid align="center">
|
<Group justify="space-between">
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} c={colors['blue-button']}>Preview Pejabat Desa</Title>
|
<Title order={3} c={colors['blue-button']}>Preview Pejabat Desa</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }}>
|
|
||||||
<Button
|
<Button
|
||||||
style={{fontSize: 15, fontWeight: "bold"}}
|
style={{ fontSize: 15, fontWeight: "bold" }}
|
||||||
c="green"
|
c="green"
|
||||||
variant="light"
|
variant="light"
|
||||||
radius="md"
|
radius="md"
|
||||||
@@ -43,8 +40,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
{dataArray.map((item) => (
|
{dataArray.map((item) => (
|
||||||
<Paper key={item.id} p="xl" bg={'white'} withBorder radius="md" shadow="xs">
|
<Paper key={item.id} p="xl" bg={'white'} withBorder radius="md" shadow="xs">
|
||||||
<Box px={{ base: "sm", md: 100 }}>
|
<Box px={{ base: "sm", md: 100 }}>
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export default function EditDataLingkunganDesa() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ function DetailDataLingkunganDesa() {
|
|||||||
const data = stateDataLingkungan.findUnique.data;
|
const data = stateDataLingkungan.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Back Button */}
|
{/* Back Button */}
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
@@ -93,7 +93,7 @@ function DetailDataLingkunganDesa() {
|
|||||||
{/* Main Card */}
|
{/* Main Card */}
|
||||||
<Paper
|
<Paper
|
||||||
withBorder
|
withBorder
|
||||||
w={{ base: '100%', md: '60%' }}
|
w={{ base: '100%', md: '70%' }}
|
||||||
bg={colors['white-1']}
|
bg={colors['white-1']}
|
||||||
p="lg"
|
p="lg"
|
||||||
radius="md"
|
radius="md"
|
||||||
@@ -131,8 +131,10 @@ function DetailDataLingkunganDesa() {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">Deskripsi</Text>
|
<Text fz="lg" fw="bold">Deskripsi</Text>
|
||||||
|
<Box pl={5}>
|
||||||
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data?.deskripsi || '-' }} />
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data?.deskripsi || '-' }} />
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<Group gap="sm" mt="sm">
|
<Group gap="sm" mt="sm">
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ function CreateDataLingkunganDesa() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,30 +1,65 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import {
|
import {
|
||||||
Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack,
|
Box,
|
||||||
Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text,
|
Button,
|
||||||
Title
|
Center,
|
||||||
|
Group,
|
||||||
|
Pagination,
|
||||||
|
Paper,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Table,
|
||||||
|
TableTbody,
|
||||||
|
TableTd,
|
||||||
|
TableTh,
|
||||||
|
TableThead,
|
||||||
|
TableTr,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconChartLine, IconChristmasTreeFilled, IconClipboardTextFilled,
|
IconAlertTriangle,
|
||||||
IconDeviceImacCog, IconDroplet, IconHome, IconHomeEco, IconLeaf,
|
IconAmbulance,
|
||||||
|
IconBuilding,
|
||||||
|
IconCash,
|
||||||
|
IconChartLine,
|
||||||
|
IconChristmasTreeFilled,
|
||||||
|
IconClipboardTextFilled,
|
||||||
|
IconDeviceImacCog,
|
||||||
|
IconDroplet,
|
||||||
|
IconFiretruck,
|
||||||
|
IconHome,
|
||||||
|
IconHomeEco,
|
||||||
|
IconHospital,
|
||||||
|
IconLeaf,
|
||||||
IconPlus,
|
IconPlus,
|
||||||
IconRecycle, IconScale, IconSearch, IconShieldFilled, IconTent,
|
IconRecycle,
|
||||||
IconTrashFilled, IconTree, IconTrendingUp, IconTrophy, IconTruckFilled
|
IconScale,
|
||||||
|
IconSchool,
|
||||||
|
IconSearch,
|
||||||
|
IconShieldFilled,
|
||||||
|
IconShoppingCart,
|
||||||
|
IconTent,
|
||||||
|
IconTrashFilled,
|
||||||
|
IconTree,
|
||||||
|
IconTrendingUp,
|
||||||
|
IconTrophy,
|
||||||
|
IconTruckFilled,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import HeaderSearch from '../../_com/header';
|
import HeaderSearch from '../../_com/header';
|
||||||
import dataLingkunganDesaState from '../../_state/lingkungan/data-lingkungan-desa';
|
import dataLingkunganDesaState from '../../_state/lingkungan/data-lingkungan-desa';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
|
|
||||||
function DataLingkunganDesa() {
|
function DataLingkunganDesa() {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState('');
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box py={{ base: 'md', md: 'lg' }} px={{ base: 'sm', md: 0 }}>
|
||||||
<HeaderSearch
|
<HeaderSearch
|
||||||
title='Data Lingkungan Desa'
|
title='Data Lingkungan Desa'
|
||||||
placeholder='Cari data lingkungan...'
|
placeholder='Cari data lingkungan...'
|
||||||
@@ -38,15 +73,16 @@ function DataLingkunganDesa() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListDataLingkunganDesa({ search }: { search: string }) {
|
function ListDataLingkunganDesa({ search }: { search: string }) {
|
||||||
const listState = useProxy(dataLingkunganDesaState)
|
const listState = useProxy(dataLingkunganDesaState);
|
||||||
const { data, loading, page, totalPages, load } = listState.findMany
|
const { data, loading, page, totalPages, load } = listState.findMany;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load(page, 10, search)
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search])
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || []
|
const filteredData = data || [];
|
||||||
|
|
||||||
const iconMap: Record<string, React.FC<any>> = {
|
const iconMap: Record<string, React.FC<any>> = {
|
||||||
ekowisata: IconLeaf,
|
ekowisata: IconLeaf,
|
||||||
@@ -64,12 +100,22 @@ function ListDataLingkunganDesa({ search }: { search: string }) {
|
|||||||
mencegahBencana: IconShieldFilled,
|
mencegahBencana: IconShieldFilled,
|
||||||
rumah: IconHome,
|
rumah: IconHome,
|
||||||
pohon: IconTree,
|
pohon: IconTree,
|
||||||
air: IconDroplet
|
air: IconDroplet,
|
||||||
|
bantuan: IconCash,
|
||||||
|
pelatihan: IconSchool,
|
||||||
|
subsidi: IconShoppingCart,
|
||||||
|
layananKesehatan: IconHospital,
|
||||||
|
polisi: IconShieldFilled,
|
||||||
|
ambulans: IconAmbulance,
|
||||||
|
pemadam: IconFiretruck,
|
||||||
|
rumahSakit: IconHospital,
|
||||||
|
bangunan: IconBuilding,
|
||||||
|
darurat: IconAlertTriangle
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py="md">
|
||||||
<Skeleton height={600} radius="md" />
|
<Skeleton height={600} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
@@ -77,46 +123,66 @@ function ListDataLingkunganDesa({ search }: { search: string }) {
|
|||||||
|
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py="md">
|
||||||
<Paper withBorder shadow="md" radius="md" p="lg" bg={colors['white-1']}>
|
<Paper withBorder shadow="md" radius="md" p={{ base: 'md', md: 'lg' }} bg={colors['white-1']}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb="md">
|
||||||
<Title order={4}>Daftar Data Lingkungan Desa</Title>
|
<Title order={4} lh={1.2}>
|
||||||
<Button leftSection={<IconPlus size={18} />} color="blue" variant="light" onClick={() => router.push('/admin/lingkungan/data-lingkungan-desa/create')}>
|
Daftar Data Lingkungan Desa
|
||||||
|
</Title>
|
||||||
|
<Button
|
||||||
|
leftSection={<IconPlus size={18} />}
|
||||||
|
color="blue"
|
||||||
|
variant="light"
|
||||||
|
onClick={() => router.push('/admin/lingkungan/data-lingkungan-desa/create')}
|
||||||
|
>
|
||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
<Table highlightOnHover>
|
<Box visibleFrom="md">
|
||||||
|
<Table highlightOnHover miw={0} style={{ tableLayout: 'fixed', width: '100%' }}>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>No</TableTh>
|
<TableTh style={{ width: '5%', textAlign: 'center' }}>No</TableTh>
|
||||||
<TableTh>Nama Data Lingkungan Desa</TableTh>
|
<TableTh style={{ width: '25%' }}>Nama Data Lingkungan Desa</TableTh>
|
||||||
<TableTh>Jumlah</TableTh>
|
<TableTh style={{ width: '35%' }}>Jumlah</TableTh>
|
||||||
<TableTh>Ikon</TableTh>
|
<TableTh style={{ width: '15%' }}>Ikon</TableTh>
|
||||||
<TableTh>Detail</TableTh>
|
<TableTh style={{ width: '20%', textAlign: 'center' }}>Detail</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
</Table>
|
</Table>
|
||||||
<Center py={20}>
|
</Box>
|
||||||
<Text c="dimmed">Tidak ada data lingkungan desa yang tersedia</Text>
|
<Center py="xl">
|
||||||
|
<Text c="dimmed" fz={{ base: 'sm', md: 'md' }} lh={1.4}>
|
||||||
|
Tidak ada data lingkungan desa yang tersedia
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box >
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py="md">
|
||||||
<Paper withBorder shadow="md" radius="md" bg={colors['white-1']} p="lg">
|
<Paper withBorder shadow="md" radius="md" p={{ base: 'md', md: 'lg' }} bg={colors['white-1']}>
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb="md">
|
||||||
<Title order={4}>Daftar Data Lingkungan Desa</Title>
|
<Title order={4} lh={1.2}>
|
||||||
<Button leftSection={<IconPlus size={18} />} color="blue" variant="light" onClick={() => router.push('/admin/lingkungan/data-lingkungan-desa/create')}>
|
Daftar Data Lingkungan Desa
|
||||||
|
</Title>
|
||||||
|
<Button
|
||||||
|
leftSection={<IconPlus size={18} />}
|
||||||
|
color="blue"
|
||||||
|
variant="light"
|
||||||
|
onClick={() => router.push('/admin/lingkungan/data-lingkungan-desa/create')}
|
||||||
|
>
|
||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
|
||||||
<Table highlightOnHover>
|
{/* Desktop Table */}
|
||||||
|
<Box visibleFrom="md">
|
||||||
|
<Table highlightOnHover miw={0} style={{ tableLayout: 'fixed', width: '100%' }}>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '5%', textAlign: 'center' }}>No</TableTh>
|
<TableTh style={{ width: '5%', textAlign: 'center' }}>No</TableTh>
|
||||||
@@ -129,21 +195,21 @@ function ListDataLingkunganDesa({ search }: { search: string }) {
|
|||||||
<TableTbody>
|
<TableTbody>
|
||||||
{filteredData.map((item, index) => (
|
{filteredData.map((item, index) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd style={{ textAlign: 'center' }}>{index + 1}</TableTd>
|
<TableTd ta="center">{index + 1}</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>{item.name}</Text>
|
<Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fz="sm" c="dimmed">± {item.jumlah}</Text>
|
<Text fz="sm" c="dimmed" lh={1.5}>
|
||||||
|
± {item.jumlah}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
{iconMap[item.icon] && (
|
{iconMap[item.icon] && <Box title={item.icon}>{React.createElement(iconMap[item.icon], { size: 22 })}</Box>}
|
||||||
<Box title={item.icon}>
|
|
||||||
{React.createElement(iconMap[item.icon], { size: 22 })}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ textAlign: 'center' }}>
|
<TableTd ta="center">
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
radius="md"
|
radius="md"
|
||||||
@@ -160,7 +226,62 @@ function ListDataLingkunganDesa({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Cards */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="sm">
|
||||||
|
{filteredData.map((item, index) => (
|
||||||
|
<Paper key={item.id} withBorder radius="md" p="sm" bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
No
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{index + 1}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Data
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} truncate="end">
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Jumlah
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
± {item.jumlah}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Ikon
|
||||||
|
</Text>
|
||||||
|
{iconMap[item.icon] && <Box title={item.icon}>{React.createElement(iconMap[item.icon], { size: 22 })}</Box>}
|
||||||
|
</Box>
|
||||||
|
<Center>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
leftSection={<IconDeviceImacCog size={16} />}
|
||||||
|
onClick={() => router.push(`/admin/lingkungan/data-lingkungan-desa/${item.id}`)}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Button>
|
||||||
|
</Center>
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
@@ -178,4 +299,5 @@ function ListDataLingkunganDesa({ search }: { search: string }) {
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DataLingkunganDesa;
|
export default DataLingkunganDesa;
|
||||||
@@ -59,6 +59,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
|
<Box visibleFrom='md' pb={10}>
|
||||||
<ScrollArea type="auto" offsetScrollbars>
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
<TabsList
|
<TabsList
|
||||||
p="sm"
|
p="sm"
|
||||||
@@ -69,7 +70,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "nowrap",
|
flexWrap: "nowrap",
|
||||||
gap: "0.5rem",
|
gap: "0.5rem",
|
||||||
paddingInline: "0.5rem",
|
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
@@ -80,22 +81,54 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
style={{
|
style={{
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
whiteSpace: "nowrap",
|
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{
|
|
||||||
display: "inline-block",
|
|
||||||
maxWidth: "200px",
|
|
||||||
overflow: "hidden",
|
|
||||||
textOverflow: "ellipsis"
|
|
||||||
}}>
|
|
||||||
{tab.label}
|
{tab.label}
|
||||||
</span>
|
|
||||||
</TabsTab>
|
</TabsTab>
|
||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box hiddenFrom='md' pb={10}>
|
||||||
|
<ScrollArea
|
||||||
|
type="auto"
|
||||||
|
offsetScrollbars={false}
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TabsList
|
||||||
|
p="xs" // lebih kecil
|
||||||
|
style={{
|
||||||
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
gap: "0.5rem",
|
||||||
|
width: "max-content", // ⬅️ kunci
|
||||||
|
maxWidth: "100%", // ⬅️ penting
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<TabsTab
|
||||||
|
key={i}
|
||||||
|
value={tab.value}
|
||||||
|
leftSection={tab.icon}
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
paddingInline: "0.75rem", // ⬅️ lebih ramping
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel key={i} value={tab.value}>
|
<TabsPanel key={i} value={tab.value}>
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export default function EditContohKegiatanDesaDarmasaba() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Group, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -25,13 +25,10 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Box p="md">
|
<Box p="md">
|
||||||
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
||||||
<Grid align="center" mb={{ base: 'md', md: 'lg' }}>
|
<Group justify="space-between" align="center" mb={{ base: 'md', md: 'lg' }}>
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} fw={600}>
|
<Title order={3} fw={600}>
|
||||||
Preview Contoh Kegiatan Di Desa Darmasaba
|
Preview Contoh Kegiatan Di Desa Darmasaba
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }} style={{ textAlign: 'right' }}>
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -46,8 +43,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
||||||
|
|||||||
@@ -1,7 +1,28 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
import LayoutTabs from "./_lib/layouTabs";
|
import LayoutTabs from "./_lib/layouTabs";
|
||||||
|
import { Box } from "@mantine/core";
|
||||||
|
|
||||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Contoh path:
|
||||||
|
// - /darmasaba/desa/berita/semua → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
|
||||||
|
|
||||||
|
const segments = pathname.split('/').filter(Boolean);
|
||||||
|
const isDetailPage = segments.length >= 5;
|
||||||
|
|
||||||
|
if (isDetailPage) {
|
||||||
|
// Tampilkan tanpa tab menu
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutTabs>
|
<LayoutTabs>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export default function EditMateriEdukasiYangDiberikan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Group, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -25,11 +25,8 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Box p="md">
|
<Box p="md">
|
||||||
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
||||||
<Grid align="center" mb={{ base: 'md', md: 'lg' }}>
|
<Group align="center" justify='space-between' mb={{ base: 'md', md: 'lg' }}>
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} fw={600}>Preview Materi Edukasi Yang Diberikan</Title>
|
<Title order={3} fw={600}>Preview Materi Edukasi Yang Diberikan</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }} style={{ textAlign: 'right' }}>
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -42,8 +39,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export default function EditTujuanEdukasiLingkungan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Group, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -25,11 +25,8 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Box p="md">
|
<Box p="md">
|
||||||
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
||||||
<Grid align="center" mb={{ base: 'md', md: 'lg' }}>
|
<Group align="center" justify='space-between' mb={{ base: 'md', md: 'lg' }}>
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} fw={600}>Preview Tujuan Edukasi Lingkungan</Title>
|
<Title order={3} fw={600}>Preview Tujuan Edukasi Lingkungan</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }} style={{ textAlign: 'right' }}>
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -42,8 +39,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
import { IconClipboardList, IconTags } from '@tabler/icons-react';
|
import { IconClipboardList, IconTags } from '@tabler/icons-react';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
@@ -59,6 +59,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
{/* ✅ Scroll horizontal */}
|
{/* ✅ Scroll horizontal */}
|
||||||
|
<Box visibleFrom='md' pb={10}>
|
||||||
<ScrollArea type="auto" offsetScrollbars>
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
<TabsList
|
<TabsList
|
||||||
p="sm"
|
p="sm"
|
||||||
@@ -69,7 +70,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "nowrap",
|
flexWrap: "nowrap",
|
||||||
gap: "0.5rem",
|
gap: "0.5rem",
|
||||||
paddingInline: "0.5rem",
|
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
@@ -81,6 +82,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
@@ -88,6 +90,45 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</ScrollArea>
|
</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>
|
||||||
|
|
||||||
{/* ✅ Panel dengan gaya kartu */}
|
{/* ✅ Panel dengan gaya kartu */}
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ function EditKategoriKegiatan() {
|
|||||||
if (loading) return <Text>Loading...</Text>;
|
if (loading) return <Text>Loading...</Text>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md" align="center">
|
<Group mb="md" align="center">
|
||||||
<Button variant="subtle" p="xs" radius="md" onClick={() => router.back()}>
|
<Button variant="subtle" p="xs" radius="md" onClick={() => router.back()}>
|
||||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ function CreateKategoriKegiatan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md" align="center">
|
<Group mb="md" align="center">
|
||||||
<Button variant="subtle" p="xs" radius="md" onClick={() => router.back()}>
|
<Button variant="subtle" p="xs" radius="md" onClick={() => router.back()}>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Title
|
Title
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -28,7 +28,7 @@ import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
|||||||
import gotongRoyongState from '../../../_state/lingkungan/gotong-royong';
|
import gotongRoyongState from '../../../_state/lingkungan/gotong-royong';
|
||||||
|
|
||||||
function KategoriKegiatan() {
|
function KategoriKegiatan() {
|
||||||
const [search, setSearch] = useState("")
|
const [search, setSearch] = useState("");
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<HeaderSearch
|
<HeaderSearch
|
||||||
@@ -44,10 +44,11 @@ function KategoriKegiatan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListKategoriKegiatan({ search }: { search: string }) {
|
function ListKategoriKegiatan({ search }: { search: string }) {
|
||||||
const stateKategori = useProxy(gotongRoyongState.kategoriKegiatan)
|
const stateKategori = useProxy(gotongRoyongState.kategoriKegiatan);
|
||||||
const [modalHapus, setModalHapus] = useState(false)
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -55,52 +56,76 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
totalPages,
|
totalPages,
|
||||||
loading,
|
loading,
|
||||||
load,
|
load,
|
||||||
} = stateKategori.findMany
|
} = stateKategori.findMany;
|
||||||
|
|
||||||
const handleHapus = () => {
|
const handleHapus = () => {
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
stateKategori.delete.byId(selectedId)
|
stateKategori.delete.byId(selectedId);
|
||||||
setModalHapus(false)
|
setModalHapus(false);
|
||||||
setSelectedId(null)
|
setSelectedId(null);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10, search)
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search])
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || []
|
const filteredData = data || [];
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={{ base: 'md', md: 'lg' }}>
|
||||||
<Skeleton height={500} radius="md" />
|
<Skeleton height={500} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={{ base: 'md', md: 'lg' }}>
|
||||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb="md">
|
||||||
<Title order={4}>Daftar Kategori Kegiatan</Title>
|
<Title order={4} lh={1.2}>
|
||||||
|
Daftar Kategori Kegiatan
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => router.push('/admin/lingkungan/gotong-royong/kategori-kegiatan/create')}
|
onClick={() =>
|
||||||
|
router.push('/admin/lingkungan/gotong-royong/kategori-kegiatan/create')
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
{/* Desktop Table */}
|
||||||
<Table highlightOnHover>
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '60%' }}>Nama Kategori</TableTh>
|
<TableTh style={{ width: '60%' }}>
|
||||||
<TableTh style={{ width: '20%' }}>Edit</TableTh>
|
<Text fz="sm" fw={600} lh={1.2} ta="left">
|
||||||
<TableTh style={{ width: '20%' }}>Delete</TableTh>
|
Nama Kategori
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '20%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.2} ta="center">
|
||||||
|
Edit
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '20%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.2} ta="center">
|
||||||
|
Delete
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
@@ -108,21 +133,27 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>{item.nama}</Text>
|
<Text fz="md" fw={500} lh={1.5} truncate="end">
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd ta="center">
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
radius="md"
|
radius="md"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="green"
|
||||||
leftSection={<IconEdit size={16} />}
|
leftSection={<IconEdit size={16} />}
|
||||||
onClick={() => router.push(`/admin/lingkungan/gotong-royong/kategori-kegiatan/${item.id}`)}
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/lingkungan/gotong-royong/kategori-kegiatan/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd ta="center">
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
radius="md"
|
radius="md"
|
||||||
@@ -130,8 +161,8 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
color="red"
|
color="red"
|
||||||
leftSection={<IconTrash size={16} />}
|
leftSection={<IconTrash size={16} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedId(item.id)
|
setSelectedId(item.id);
|
||||||
setModalHapus(true)
|
setModalHapus(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Hapus
|
Hapus
|
||||||
@@ -142,8 +173,10 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={3}>
|
<TableTd colSpan={3}>
|
||||||
<Center py={20}>
|
<Center py="xl">
|
||||||
<Text c="dimmed">Tidak ada kategori kegiatan yang cocok</Text>
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada kategori kegiatan yang cocok
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -151,6 +184,63 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Cards */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="sm">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
|
<Paper key={item.id} withBorder p="md" radius="md">
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Kategori
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.45}>
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Group justify="flex-end" mt="xs">
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
|
variant="light"
|
||||||
|
color="green"
|
||||||
|
leftSection={<IconEdit size={16} />}
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/lingkungan/gotong-royong/kategori-kegiatan/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
|
variant="light"
|
||||||
|
color="red"
|
||||||
|
leftSection={<IconTrash size={16} />}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(item.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py="xl">
|
||||||
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada kategori kegiatan yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ export default function EditKegiatanDesa() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function DetailKegiatanDesa() {
|
|||||||
const data = kegiatanDesaState.findUnique.data;
|
const data = kegiatanDesaState.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Tombol Kembali */}
|
{/* Tombol Kembali */}
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
@@ -55,7 +55,7 @@ function DetailKegiatanDesa() {
|
|||||||
{/* Container Detail */}
|
{/* Container Detail */}
|
||||||
<Paper
|
<Paper
|
||||||
withBorder
|
withBorder
|
||||||
w={{ base: "100%", md: "60%" }}
|
w={{ base: "100%", md: "70%" }}
|
||||||
bg={colors['white-1']}
|
bg={colors['white-1']}
|
||||||
p="lg"
|
p="lg"
|
||||||
radius="md"
|
radius="md"
|
||||||
@@ -85,14 +85,18 @@ function DetailKegiatanDesa() {
|
|||||||
{/* Deskripsi Singkat */}
|
{/* Deskripsi Singkat */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">Deskripsi Singkat</Text>
|
<Text fz="lg" fw="bold">Deskripsi Singkat</Text>
|
||||||
|
<Box pl={5}>
|
||||||
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.deskripsiSingkat || '-' }} />
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.deskripsiSingkat || '-' }} />
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* Deskripsi Lengkap */}
|
{/* Deskripsi Lengkap */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">Deskripsi</Text>
|
<Text fz="lg" fw="bold">Deskripsi</Text>
|
||||||
|
<Box pl={5}>
|
||||||
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.deskripsiLengkap || '-' }} />
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.deskripsiLengkap || '-' }} />
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* Kategori */}
|
{/* Kategori */}
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ function CreateKegiatanDesa() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Title,
|
Title,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -45,6 +45,7 @@ function KegiatanDesa() {
|
|||||||
function ListKegiatanDesa({ search }: { search: string }) {
|
function ListKegiatanDesa({ search }: { search: string }) {
|
||||||
const listState = useProxy(gotongRoyongState.kegiatanDesa);
|
const listState = useProxy(gotongRoyongState.kegiatanDesa);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -55,24 +56,26 @@ function ListKegiatanDesa({ search }: { search: string }) {
|
|||||||
} = listState.findMany;
|
} = listState.findMany;
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10, search)
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search])
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || []
|
const filteredData = data || [];
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={{ base: 'sm', md: 'md' }}>
|
||||||
<Skeleton height={600} radius="md" />
|
<Skeleton height={600} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={{ base: 'sm', md: 'md' }}>
|
||||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p={{ base: 'sm', md: 'lg' }} shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||||
<Title order={4}>Daftar Kegiatan Desa</Title>
|
<Title order={4} lh={1.2}>
|
||||||
|
Daftar Kegiatan Desa
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
@@ -84,14 +87,39 @@ function ListKegiatanDesa({ search }: { search: string }) {
|
|||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
|
||||||
<Table highlightOnHover>
|
{/* Desktop: Table */}
|
||||||
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '30%' }}>Judul</TableTh>
|
<TableTh style={{ width: '30%' }}>
|
||||||
<TableTh style={{ width: '25%' }}>Kategori</TableTh>
|
<Text fz="sm" fw={600} lh={1.4} c="dark">
|
||||||
<TableTh style={{ width: '25%' }}>Lokasi</TableTh>
|
Judul
|
||||||
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '25%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} c="dark">
|
||||||
|
Kategori
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '25%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} c="dark">
|
||||||
|
Lokasi
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '20%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} c="dark">
|
||||||
|
Aksi
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
@@ -99,17 +127,36 @@ function ListKegiatanDesa({ search }: { search: string }) {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>
|
<Text
|
||||||
|
fz="md"
|
||||||
|
fw={500}
|
||||||
|
lh={1.5}
|
||||||
|
truncate="end"
|
||||||
|
lineClamp={1}
|
||||||
|
c="dark"
|
||||||
|
>
|
||||||
{item.judul}
|
{item.judul}
|
||||||
</Text>
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fz="sm" c="dimmed">
|
<Text
|
||||||
|
fz="sm"
|
||||||
|
fw={500}
|
||||||
|
lh={1.5}
|
||||||
|
c="dark"
|
||||||
|
>
|
||||||
{item.kategoriKegiatan?.nama || '-'}
|
{item.kategoriKegiatan?.nama || '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fz="sm">{item.lokasi}</Text>
|
<Text
|
||||||
|
fz="sm"
|
||||||
|
fw={500}
|
||||||
|
lh={1.5}
|
||||||
|
c="dark"
|
||||||
|
>
|
||||||
|
{item.lokasi}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
@@ -133,7 +180,7 @@ function ListKegiatanDesa({ search }: { search: string }) {
|
|||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={4}>
|
<TableTd colSpan={4}>
|
||||||
<Center py={20}>
|
<Center py={20}>
|
||||||
<Text c="dimmed">
|
<Text fz="sm" c="dimmed" lh={1.4}>
|
||||||
Tidak ada kegiatan desa yang cocok dengan pencarian
|
Tidak ada kegiatan desa yang cocok dengan pencarian
|
||||||
</Text>
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
@@ -143,6 +190,64 @@ function ListKegiatanDesa({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile: Card List */}
|
||||||
|
<Stack hiddenFrom="md" gap="xs">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
|
<Paper key={item.id} withBorder radius="md" p="sm">
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} c="dark">
|
||||||
|
Judul
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} c="dark" truncate="end">
|
||||||
|
{item.judul}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} c="dark">
|
||||||
|
Kategori
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} c="dark">
|
||||||
|
{item.kategoriKegiatan?.nama || '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} c="dark">
|
||||||
|
Lokasi
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} c="dark">
|
||||||
|
{item.lokasi}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
leftSection={<IconDeviceImacCog size={16} />}
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/lingkungan/gotong-royong/kegiatan-desa/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py={20}>
|
||||||
|
<Text fz="sm" c="dimmed" lh={1.4}>
|
||||||
|
Tidak ada kegiatan desa yang cocok dengan pencarian
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Center>
|
<Center>
|
||||||
<Pagination
|
<Pagination
|
||||||
|
|||||||
@@ -1,6 +1,28 @@
|
|||||||
|
'use client'
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
import LayoutTabs from "./_lib/layoutTabs";
|
import LayoutTabs from "./_lib/layoutTabs";
|
||||||
|
import { Box } from "@mantine/core";
|
||||||
|
|
||||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Contoh path:
|
||||||
|
// - /darmasaba/desa/berita/semua → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
|
||||||
|
|
||||||
|
const segments = pathname.split('/').filter(Boolean);
|
||||||
|
const isDetailPage = segments.length >= 5;
|
||||||
|
|
||||||
|
if (isDetailPage) {
|
||||||
|
// Tampilkan tanpa tab menu
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutTabs>
|
<LayoutTabs>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
|
<Box visibleFrom='md' pb={10}>
|
||||||
<ScrollArea type="auto" offsetScrollbars>
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
<TabsList
|
<TabsList
|
||||||
p="sm"
|
p="sm"
|
||||||
@@ -69,7 +70,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "nowrap",
|
flexWrap: "nowrap",
|
||||||
gap: "0.5rem",
|
gap: "0.5rem",
|
||||||
paddingInline: "0.5rem",
|
paddingInline: "0.5rem", // ✅ biar nggak nempel ke tepi
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
@@ -80,22 +81,54 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
style={{
|
style={{
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
whiteSpace: "nowrap",
|
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{
|
|
||||||
display: "inline-block",
|
|
||||||
maxWidth: "200px",
|
|
||||||
overflow: "hidden",
|
|
||||||
textOverflow: "ellipsis"
|
|
||||||
}}>
|
|
||||||
{tab.label}
|
{tab.label}
|
||||||
</span>
|
|
||||||
</TabsTab>
|
</TabsTab>
|
||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box hiddenFrom='md' pb={10}>
|
||||||
|
<ScrollArea
|
||||||
|
type="auto"
|
||||||
|
offsetScrollbars={false}
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TabsList
|
||||||
|
p="xs" // lebih kecil
|
||||||
|
style={{
|
||||||
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
gap: "0.5rem",
|
||||||
|
width: "max-content", // ⬅️ kunci
|
||||||
|
maxWidth: "100%", // ⬅️ penting
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<TabsTab
|
||||||
|
key={i}
|
||||||
|
value={tab.value}
|
||||||
|
leftSection={tab.icon}
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
paddingInline: "0.75rem", // ⬅️ lebih ramping
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel key={i} value={tab.value}>
|
<TabsPanel key={i} value={tab.value}>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ function EditBentukKonservasiBerdasarkanAdat() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Group, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -28,13 +28,10 @@ function Page() {
|
|||||||
<Box p="md">
|
<Box p="md">
|
||||||
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Grid align="center" mb={{ base: 'md', md: 'lg' }}>
|
<Group justify='space-between' align="center" mb={{ base: 'md', md: 'lg' }}>
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} fw={600} c="dark">
|
<Title order={3} fw={600} c="dark">
|
||||||
Preview Bentuk Konservasi Berdasarkan Adat
|
Preview Bentuk Konservasi Berdasarkan Adat
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }} style={{ textAlign: 'right' }}>
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -49,8 +46,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Konten */}
|
{/* Konten */}
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ function EditFilosofiTriHitaKarana() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Group, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -25,13 +25,10 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Box p="md">
|
<Box p="md">
|
||||||
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
||||||
<Grid align="center" mb={{ base: 'md', md: 'lg' }}>
|
<Group justify="space-between" align="center" mb={{ base: 'md', md: 'lg' }}>
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} fw={600}>
|
<Title order={3} fw={600}>
|
||||||
Preview Filosofi Tri Hita Karana
|
Preview Filosofi Tri Hita Karana
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }} style={{ textAlign: 'right' }}>
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -46,8 +43,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
||||||
|
|||||||
@@ -1,8 +1,29 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
import LayoutTabs from "./_lib/layoutTabs"
|
import LayoutTabs from "./_lib/layoutTabs"
|
||||||
|
import { Box } from "@mantine/core";
|
||||||
|
|
||||||
|
|
||||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Contoh path:
|
||||||
|
// - /darmasaba/desa/berita/semua → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
|
||||||
|
|
||||||
|
const segments = pathname.split('/').filter(Boolean);
|
||||||
|
const isDetailPage = segments.length >= 5;
|
||||||
|
|
||||||
|
if (isDetailPage) {
|
||||||
|
// Tampilkan tanpa tab menu
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutTabs>
|
<LayoutTabs>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ function EditNilaiKonservasiAdat() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Group, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -25,13 +25,10 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Box p="md">
|
<Box p="md">
|
||||||
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
<Paper withBorder p={{ base: 'md', md: 'lg' }} radius="md">
|
||||||
<Grid align="center" mb={{ base: 'md', md: 'lg' }}>
|
<Group align="center" justify='space-between' mb={{ base: 'md', md: 'lg' }}>
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} fw={600}>
|
<Title order={3} fw={600}>
|
||||||
Preview Nilai Konservasi Adat
|
Preview Nilai Konservasi Adat
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }} style={{ textAlign: 'right' }}>
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -44,8 +41,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
<Paper p={{ base: 'md', md: 'xl' }} bg="#ECEEF8" radius="md">
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ function EditKeteranganBankSampahTerdekat() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ function DetailKeteranganBankSampahTerdekat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Box mb="md">
|
<Box mb="md">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -57,7 +57,7 @@ function DetailKeteranganBankSampahTerdekat() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Paper
|
<Paper
|
||||||
w={{ base: "100%", md: "60%" }}
|
w={{ base: "100%", md: "70%" }}
|
||||||
p="xl"
|
p="xl"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
withBorder
|
withBorder
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ function CreateKeteranganBankSampahTerdekat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
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 { useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -28,6 +28,7 @@ function KeteranganBankSampahTerdekat() {
|
|||||||
function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
|
function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
|
||||||
const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah);
|
const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -38,24 +39,26 @@ function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
|
|||||||
} = keteranganState.findMany;
|
} = keteranganState.findMany;
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10, search);
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search]);
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || [];
|
const filteredData = data || [];
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={{ base: 'sm', md: 'md' }}>
|
||||||
<Skeleton height={600} radius="md" />
|
<Skeleton height={600} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={{ base: 'sm', md: 'md' }}>
|
||||||
<Paper bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
<Paper bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||||
<Title order={4}>Daftar Bank Sampah Terdekat</Title>
|
<Title order={4} lh={1.2}>
|
||||||
|
Daftar Bank Sampah Terdekat
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
@@ -66,14 +69,22 @@ function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
{/* Desktop Table */}
|
||||||
<Table highlightOnHover>
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>Nama Bank Sampah</TableTh>
|
<TableTh style={{ width: '30%' }}>Nama Bank Sampah</TableTh>
|
||||||
<TableTh>Alamat</TableTh>
|
<TableTh style={{ width: '35%' }}>Alamat</TableTh>
|
||||||
<TableTh>Nama Tempat di Maps</TableTh>
|
<TableTh style={{ width: '25%' }}>Nama Tempat di Maps</TableTh>
|
||||||
<TableTh>Aksi</TableTh>
|
<TableTh style={{ width: '10%' }}>Aksi</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
@@ -81,17 +92,17 @@ function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500}>{item.name}</Text>
|
<Text fz="md" fw={500} lh={1.5}>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box w={200}>
|
<Text fz="sm" lh={1.5} lineClamp={2}>
|
||||||
<Text lineClamp={2} truncate="end" fz="sm">
|
|
||||||
{item.alamat || '-'}
|
{item.alamat || '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fz="sm">
|
<Text fz="sm" lh={1.5}>
|
||||||
{item.namaTempatMaps || '-'}
|
{item.namaTempatMaps || '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
@@ -100,6 +111,7 @@ function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
|
|||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
onClick={() => router.push(`/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/${item.id}`)}
|
onClick={() => router.push(`/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/${item.id}`)}
|
||||||
|
p="xs"
|
||||||
>
|
>
|
||||||
<IconDeviceImac size={20} />
|
<IconDeviceImac size={20} />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -108,8 +120,10 @@ function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
|
|||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={4} align="center" py="xl">
|
<TableTd colSpan={4} ta="center" py="xl">
|
||||||
<Text c="dimmed">Tidak ada data bank sampah terdekat</Text>
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data bank sampah terdekat
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
)}
|
)}
|
||||||
@@ -117,8 +131,60 @@ function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
|
|||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{totalPages > 1 && (
|
{/* Mobile Card */}
|
||||||
<Center mt="xl">
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="xs">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
|
<Paper key={item.id} p="sm" radius="sm" withBorder>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Bank Sampah
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Alamat
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.alamat || '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Tempat di Maps
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.namaTempatMaps || '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Group justify="flex-end" mt="xs">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
onClick={() => router.push(`/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/${item.id}`)}
|
||||||
|
p="xs"
|
||||||
|
>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py="xl">
|
||||||
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data bank sampah terdekat
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
<Center mt={{ base: 'lg', md: 'xl' }}>
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
onChange={(newPage) => load(newPage)}
|
onChange={(newPage) => load(newPage)}
|
||||||
@@ -128,7 +194,6 @@ function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
|
|||||||
withEdges
|
withEdges
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
)}
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ function EditProgramKreatifDesa() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ function CreatePengelolaanSampahBankSampah() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
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 { useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconChartLine, IconClipboardTextFilled, IconEdit, IconLeaf, IconPlus, IconRecycle, IconScale, IconSearch, IconTent, IconTrashFilled, IconTrophy, IconTruckFilled } from '@tabler/icons-react';
|
import { IconAlertTriangle, IconAmbulance, IconBuilding, IconCash, IconChartLine, IconChristmasTreeFilled, IconClipboardTextFilled, IconDroplet, IconEdit, IconFiretruck, IconHome, IconHomeEco, IconHospital, IconLeaf, IconPlus, IconRecycle, IconScale, IconSchool, IconSearch, IconShieldFilled, IconShoppingCart, IconTent, IconTrashFilled, IconTree, IconTrendingUp, IconTrophy, IconTruckFilled } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
@@ -32,6 +32,7 @@ function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
|
|||||||
const [modalHapus, setModalHapus] = useState(false)
|
const [modalHapus, setModalHapus] = useState(false)
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -42,8 +43,8 @@ function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
|
|||||||
} = stateList.findMany
|
} = stateList.findMany
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10, search)
|
load(page, 10, debouncedSearch)
|
||||||
}, [page, search])
|
}, [page, debouncedSearch])
|
||||||
|
|
||||||
|
|
||||||
const handleHapus = () => {
|
const handleHapus = () => {
|
||||||
@@ -66,6 +67,23 @@ function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
|
|||||||
scale: IconScale,
|
scale: IconScale,
|
||||||
clipboard: IconClipboardTextFilled,
|
clipboard: IconClipboardTextFilled,
|
||||||
trash: IconTrashFilled,
|
trash: IconTrashFilled,
|
||||||
|
lingkunganSehat: IconHomeEco,
|
||||||
|
sumberOksigen: IconChristmasTreeFilled,
|
||||||
|
ekonomiBerkelanjutan: IconTrendingUp,
|
||||||
|
mencegahBencana: IconShieldFilled,
|
||||||
|
rumah: IconHome,
|
||||||
|
pohon: IconTree,
|
||||||
|
air: IconDroplet,
|
||||||
|
bantuan: IconCash,
|
||||||
|
pelatihan: IconSchool,
|
||||||
|
subsidi: IconShoppingCart,
|
||||||
|
layananKesehatan: IconHospital,
|
||||||
|
polisi: IconShieldFilled,
|
||||||
|
ambulans: IconAmbulance,
|
||||||
|
pemadam: IconFiretruck,
|
||||||
|
rumahSakit: IconHospital,
|
||||||
|
bangunan: IconBuilding,
|
||||||
|
darurat: IconAlertTriangle,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
@@ -77,28 +95,45 @@ function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={{ base: 'sm', md: 'lg' }}>
|
||||||
<Paper bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
<Paper bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb={{ base: 'md', md: 'lg' }}>
|
||||||
<Title order={4}>Daftar Pengelolaan Sampah Bank Sampah</Title>
|
<Title
|
||||||
|
order={4}
|
||||||
|
lh={{ base: 1.2, md: 1.15 }}
|
||||||
|
>
|
||||||
|
Daftar Pengelolaan Sampah Bank Sampah
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => router.push('/admin/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create')}
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
'/admin/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create'
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
{/* Desktop Table */}
|
||||||
<Table highlightOnHover>
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>Nama Pengelolaan Sampah</TableTh>
|
<TableTh style={{ width: '50%' }}>Nama Pengelolaan Sampah</TableTh>
|
||||||
<TableTh>Icon</TableTh>
|
<TableTh style={{ width: '20%' }}>Icon</TableTh>
|
||||||
<TableTh>Edit</TableTh>
|
<TableTh style={{ width: '15%' }}>Edit</TableTh>
|
||||||
<TableTh>Delete</TableTh>
|
<TableTh style={{ width: '15%' }}>Delete</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
@@ -106,18 +141,22 @@ function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text lineClamp={1} truncate="end" fw={500}>{item.name}</Text>
|
<Text fz="md" fw={500} lh={1.5} lineClamp={1} truncate="end">
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
{iconMap[item.icon] ? (
|
{iconMap[item.icon] ? (
|
||||||
<Box>
|
<Box>
|
||||||
{React.createElement(iconMap[item.icon], {
|
{React.createElement(iconMap[item.icon], {
|
||||||
size: 24,
|
size: 24,
|
||||||
style: { color: colors['blue-button'] }
|
style: { color: colors['blue-button'] },
|
||||||
})}
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Text c="dimmed" fz="sm">-</Text>
|
<Text c="dimmed" fz="sm">
|
||||||
|
-
|
||||||
|
</Text>
|
||||||
)}
|
)}
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
@@ -125,7 +164,11 @@ function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
|
|||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="green"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => router.push(`/admin/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/${item.id}`)}
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<IconEdit size={18} />
|
<IconEdit size={18} />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -147,8 +190,10 @@ function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
|
|||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={3} align="center" py="xl">
|
<TableTd colSpan={4} ta="center" py="xl">
|
||||||
<Text c="dimmed">Tidak ada data pengelolaan sampah</Text>
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data pengelolaan sampah
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
)}
|
)}
|
||||||
@@ -156,8 +201,73 @@ function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
|
|||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{totalPages > 1 && (
|
{/* Mobile Cards */}
|
||||||
<Center mt="xl">
|
<Box hiddenFrom="md">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
<Stack gap="md">
|
||||||
|
{filteredData.map((item) => (
|
||||||
|
<Paper key={item.id} p="sm" withBorder>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Pengelolaan Sampah
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.45} lineClamp={1}>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
{iconMap[item.icon] ? (
|
||||||
|
React.createElement(iconMap[item.icon], {
|
||||||
|
size: 20,
|
||||||
|
style: { color: colors['blue-button'] },
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<Text c="dimmed" fz="sm">
|
||||||
|
-
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<Group justify='right'>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="green"
|
||||||
|
size="xs"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
radius="sm"
|
||||||
|
p={4}
|
||||||
|
>
|
||||||
|
<IconEdit size={18} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="red"
|
||||||
|
size="xs"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(item.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}}
|
||||||
|
radius="sm"
|
||||||
|
p={4}
|
||||||
|
>
|
||||||
|
<IconTrashFilled size={18} />
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
) : (
|
||||||
|
<Center py="xl">
|
||||||
|
<Text c="dimmed" fz="xs" lh={1.4}>
|
||||||
|
Tidak ada data pengelolaan sampah
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
|
<Center mt={{ base: 'lg', md: 'xl' }}>
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
onChange={(newPage) => load(newPage)}
|
onChange={(newPage) => load(newPage)}
|
||||||
@@ -167,8 +277,6 @@ function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
|
|||||||
withEdges
|
withEdges
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
)}
|
|
||||||
|
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Modal Konfirmasi Hapus */}
|
{/* Modal Konfirmasi Hapus */}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ function EditProgramPenghijauan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ function DetailProgramPenghijauan() {
|
|||||||
const data = stateProgramPenghijauan.findUnique.data
|
const data = stateProgramPenghijauan.findUnique.data
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Tombol kembali */}
|
{/* Tombol kembali */}
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
@@ -93,7 +93,7 @@ function DetailProgramPenghijauan() {
|
|||||||
{/* Konten detail */}
|
{/* Konten detail */}
|
||||||
<Paper
|
<Paper
|
||||||
withBorder
|
withBorder
|
||||||
w={{ base: "100%", md: "60%" }}
|
w={{ base: "100%", md: "70%" }}
|
||||||
bg={colors['white-1']}
|
bg={colors['white-1']}
|
||||||
p="lg"
|
p="lg"
|
||||||
radius="md"
|
radius="md"
|
||||||
@@ -129,8 +129,10 @@ function DetailProgramPenghijauan() {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">Deskripsi</Text>
|
<Text fz="lg" fw="bold">Deskripsi</Text>
|
||||||
|
<Box pl={5}>
|
||||||
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data?.deskripsi || '-' }} />
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data?.deskripsi || '-' }} />
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* Tombol aksi */}
|
{/* Tombol aksi */}
|
||||||
<Group gap="sm" mt="md">
|
<Group gap="sm" mt="md">
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ function CreateProgramPenghijauan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Tombol back + title */}
|
{/* Tombol back + title */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
|
Group,
|
||||||
Pagination,
|
Pagination,
|
||||||
Paper,
|
Paper,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
@@ -17,22 +18,33 @@ import {
|
|||||||
TableThead,
|
TableThead,
|
||||||
TableTr,
|
TableTr,
|
||||||
Text,
|
Text,
|
||||||
Title
|
Title,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
|
IconAlertTriangle,
|
||||||
|
IconAmbulance,
|
||||||
|
IconBuilding,
|
||||||
|
IconCash,
|
||||||
IconChartLine,
|
IconChartLine,
|
||||||
IconChristmasTreeFilled,
|
IconChristmasTreeFilled,
|
||||||
IconClipboardTextFilled,
|
IconClipboardTextFilled,
|
||||||
IconDeviceImac,
|
IconDeviceImac,
|
||||||
|
IconDroplet,
|
||||||
|
IconFiretruck,
|
||||||
|
IconHome,
|
||||||
IconHomeEco,
|
IconHomeEco,
|
||||||
|
IconHospital,
|
||||||
IconLeaf,
|
IconLeaf,
|
||||||
IconPlus,
|
IconPlus,
|
||||||
IconRecycle,
|
IconRecycle,
|
||||||
IconScale,
|
IconScale,
|
||||||
|
IconSchool,
|
||||||
IconSearch,
|
IconSearch,
|
||||||
IconShieldFilled,
|
IconShieldFilled,
|
||||||
|
IconShoppingCart,
|
||||||
IconTent,
|
IconTent,
|
||||||
IconTrashFilled,
|
IconTrashFilled,
|
||||||
|
IconTree,
|
||||||
IconTrendingUp,
|
IconTrendingUp,
|
||||||
IconTrophy,
|
IconTrophy,
|
||||||
IconTruckFilled,
|
IconTruckFilled,
|
||||||
@@ -42,9 +54,10 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import HeaderSearch from '../../_com/header';
|
import HeaderSearch from '../../_com/header';
|
||||||
import programPenghijauanState from '../../_state/lingkungan/program-penghijauan';
|
import programPenghijauanState from '../../_state/lingkungan/program-penghijauan';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
|
|
||||||
function ProgramPenghijauan() {
|
function ProgramPenghijauan() {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState('');
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<HeaderSearch
|
<HeaderSearch
|
||||||
@@ -60,15 +73,16 @@ function ProgramPenghijauan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListProgramPenghijauan({ search }: { search: string }) {
|
function ListProgramPenghijauan({ search }: { search: string }) {
|
||||||
const listState = useProxy(programPenghijauanState)
|
const listState = useProxy(programPenghijauanState);
|
||||||
const { data, loading, page, totalPages, load } = listState.findMany
|
const { data, loading, page, totalPages, load } = listState.findMany;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load(page, 10, search)
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search])
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || []
|
const filteredData = data || [];
|
||||||
|
|
||||||
const iconMap: Record<string, React.FC<any>> = {
|
const iconMap: Record<string, React.FC<any>> = {
|
||||||
ekowisata: IconLeaf,
|
ekowisata: IconLeaf,
|
||||||
@@ -84,70 +98,134 @@ function ListProgramPenghijauan({ search }: { search: string }) {
|
|||||||
sumberOksigen: IconChristmasTreeFilled,
|
sumberOksigen: IconChristmasTreeFilled,
|
||||||
ekonomiBerkelanjutan: IconTrendingUp,
|
ekonomiBerkelanjutan: IconTrendingUp,
|
||||||
mencegahBencana: IconShieldFilled,
|
mencegahBencana: IconShieldFilled,
|
||||||
|
rumah: IconHome,
|
||||||
|
pohon: IconTree,
|
||||||
|
air: IconDroplet,
|
||||||
|
bantuan: IconCash,
|
||||||
|
pelatihan: IconSchool,
|
||||||
|
subsidi: IconShoppingCart,
|
||||||
|
layananKesehatan: IconHospital,
|
||||||
|
polisi: IconShieldFilled,
|
||||||
|
ambulans: IconAmbulance,
|
||||||
|
pemadam: IconFiretruck,
|
||||||
|
rumahSakit: IconHospital,
|
||||||
|
bangunan: IconBuilding,
|
||||||
|
darurat: IconAlertTriangle,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py="md">
|
||||||
<Skeleton height={600} radius="md" />
|
<Skeleton height={600} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py="md">
|
||||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<Box mb="md" display="flex" style={{ justifyContent: 'space-between', alignItems: 'center' }}>
|
<Box mb="lg" visibleFrom="md">
|
||||||
<Title order={4}>Daftar Program Penghijauan</Title>
|
<Group
|
||||||
|
justify="space-between"
|
||||||
|
align="center"
|
||||||
|
mb="md"
|
||||||
|
>
|
||||||
|
<Title order={4} size="md" lh={1.2}>
|
||||||
|
Daftar Program Penghijauan
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => router.push('/admin/lingkungan/program-penghijauan/create')}
|
onClick={() =>
|
||||||
|
router.push('/admin/lingkungan/program-penghijauan/create')
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Group>
|
||||||
|
|
||||||
{/* Table Section */}
|
{/* Desktop Table */}
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
<Box miw={0}>
|
||||||
<Table highlightOnHover>
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{ tableLayout: 'fixed', width: '100%' }}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '5%', textAlign: 'center' }}>No</TableTh>
|
<TableTh style={{ width: '5%', textAlign: 'center' }}>
|
||||||
<TableTh style={{ width: '25%' }}>Nama Program</TableTh>
|
<Text fz="xs" fw={600} lh={1.4} ta="center">
|
||||||
<TableTh style={{ width: '35%' }}>Judul / Deskripsi</TableTh>
|
No
|
||||||
<TableTh style={{ width: '15%', textAlign: 'center' }}>Ikon</TableTh>
|
</Text>
|
||||||
<TableTh style={{ width: '15%', textAlign: 'center' }}>Aksi</TableTh>
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '25%' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
Nama Program
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '35%' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
Judul / Deskripsi
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '15%', textAlign: 'center' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4} ta="center">
|
||||||
|
Ikon
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '20%', textAlign: 'center' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4} ta="center">
|
||||||
|
Aksi
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{filteredData.length > 0 ? (
|
{filteredData.length > 0 ? (
|
||||||
filteredData.map((item, index) => (
|
filteredData.map((item, index) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd style={{ textAlign: 'center' }}>{index + 1}</TableTd>
|
<TableTd ta="center">
|
||||||
<TableTd>
|
<Text fz="sm" fw={500} lh={1.5}>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>{item.name}</Text>
|
{index + 1}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text lineClamp={1} fz="sm" c="dimmed" dangerouslySetInnerHTML={{ __html: item.judul }} />
|
<Text fz="sm" fw={500} lh={1.5} truncate="end">
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ textAlign: 'center' }}>
|
<TableTd>
|
||||||
|
<Text
|
||||||
|
fz="sm"
|
||||||
|
fw={500}
|
||||||
|
lh={1.5}
|
||||||
|
lineClamp={1}
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.judul }}
|
||||||
|
/>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd ta="center">
|
||||||
{iconMap[item.icon] && (
|
{iconMap[item.icon] && (
|
||||||
<Box title={item.icon} mx="auto">
|
<Box mx="auto" title={item.icon}>
|
||||||
{React.createElement(iconMap[item.icon], { size: 22 })}
|
{React.createElement(iconMap[item.icon], {
|
||||||
|
size: 22,
|
||||||
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ textAlign: 'center' }}>
|
<TableTd ta="center">
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
radius="md"
|
radius="md"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
leftSection={<IconDeviceImac size={16} />}
|
leftSection={<IconDeviceImac size={16} />}
|
||||||
onClick={() => router.push(`/admin/lingkungan/program-penghijauan/${item.id}`)}
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/lingkungan/program-penghijauan/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Detail
|
Detail
|
||||||
</Button>
|
</Button>
|
||||||
@@ -157,8 +235,10 @@ function ListProgramPenghijauan({ search }: { search: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={5}>
|
<TableTd colSpan={5}>
|
||||||
<Center py={20}>
|
<Center py="xl">
|
||||||
<Text c="dimmed">Tidak ada data program penghijauan yang cocok</Text>
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data program penghijauan yang cocok
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -166,6 +246,97 @@ function ListProgramPenghijauan({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile View – Card Layout */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Box mb="md">
|
||||||
|
<Title order={4} size="sm" lh={1.2}>
|
||||||
|
Daftar Program Penghijauan
|
||||||
|
</Title>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
leftSection={<IconPlus size={18} />}
|
||||||
|
color="blue"
|
||||||
|
variant="light"
|
||||||
|
mb="lg"
|
||||||
|
fullWidth
|
||||||
|
onClick={() =>
|
||||||
|
router.push('/admin/lingkungan/program-penghijauan/create')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Tambah Baru
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item, index) => (
|
||||||
|
<Paper key={item.id} withBorder p="md" mb="sm" radius="md">
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
No
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{index + 1}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Program
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Judul / Deskripsi
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
fz="sm"
|
||||||
|
fw={500}
|
||||||
|
lh={1.5}
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.judul }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Ikon
|
||||||
|
</Text>
|
||||||
|
<Box mt="xs" ta="center">
|
||||||
|
{iconMap[item.icon] && (
|
||||||
|
React.createElement(iconMap[item.icon], { size: 24 })
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
leftSection={<IconDeviceImac size={16} />}
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/lingkungan/program-penghijauan/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py="xl">
|
||||||
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data program penghijauan yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
import { IconSchool, IconStar } from '@tabler/icons-react';
|
import { IconSchool, IconStar } from '@tabler/icons-react';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
@@ -56,6 +56,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
|
<Box visibleFrom='md' pb={10}>
|
||||||
<ScrollArea type="auto" offsetScrollbars>
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
<TabsList
|
<TabsList
|
||||||
p="sm"
|
p="sm"
|
||||||
@@ -78,6 +79,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
@@ -85,6 +87,45 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box hiddenFrom='md' pb={10}>
|
||||||
|
<ScrollArea
|
||||||
|
type="auto"
|
||||||
|
offsetScrollbars={false}
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TabsList
|
||||||
|
p="xs" // lebih kecil
|
||||||
|
style={{
|
||||||
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
gap: "0.5rem",
|
||||||
|
width: "max-content", // ⬅️ kunci
|
||||||
|
maxWidth: "100%", // ⬅️ penting
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<TabsTab
|
||||||
|
key={i}
|
||||||
|
value={tab.value}
|
||||||
|
leftSection={tab.icon}
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
paddingInline: "0.75rem", // ⬅️ lebih ramping
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function DetailBeasiswaPendaftar() {
|
|||||||
const data = state.findUnique.data;
|
const data = state.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
onClick={() => router.back()}
|
onClick={() => router.back()}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Title
|
Title
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@@ -45,60 +46,81 @@ function BeasiswaPendaftar() {
|
|||||||
function ListBeasiswaPendaftar({ search }: { search: string }) {
|
function ListBeasiswaPendaftar({ search }: { search: string }) {
|
||||||
const state = useProxy(beasiswaDesaState.beasiswaPendaftar);
|
const state = useProxy(beasiswaDesaState.beasiswaPendaftar);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const { data, page, totalPages, loading, load } = state.findMany;
|
const { data, page, totalPages, loading, load } = state.findMany;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load(page, 10, search);
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search]);
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || [];
|
const filteredData = data || [];
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={{ base: 'sm', md: 'md' }}>
|
||||||
<Skeleton height={600} radius="md" />
|
<Skeleton height={600} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={{ base: 'sm', md: 'md' }}>
|
||||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||||
<Title order={4}>Daftar Beasiswa Pendaftar</Title>
|
<Title order={4} lh={1.2}>
|
||||||
|
Daftar Beasiswa Pendaftar
|
||||||
|
</Title>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
{/* Desktop: Table */}
|
||||||
<Table highlightOnHover>
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>No</TableTh>
|
<TableTh w="5%">No</TableTh>
|
||||||
<TableTh>Nama Lengkap</TableTh>
|
<TableTh w="25%">Nama Lengkap</TableTh>
|
||||||
<TableTh>Alamat</TableTh>
|
<TableTh w="30%">Alamat</TableTh>
|
||||||
<TableTh>Tanggal Lahir</TableTh>
|
<TableTh w="15%">Tanggal Lahir</TableTh>
|
||||||
<TableTh>Jenis Kelamin</TableTh>
|
<TableTh w="15%">Jenis Kelamin</TableTh>
|
||||||
<TableTh>Aksi</TableTh>
|
<TableTh w="10%">Aksi</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{filteredData.length > 0 ? (
|
{filteredData.length > 0 ? (
|
||||||
filteredData.map((item, index) => (
|
filteredData.map((item, index) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>{index + 1}</TableTd>
|
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text truncate fz="sm">{item.namaLengkap}</Text>
|
<Text fz="md" fw={500} lh={1.45}>
|
||||||
|
{index + 1}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text truncate fz="sm">{item.alamatKTP}</Text>
|
<Text truncate fz="md" fw={500} lh={1.45}>
|
||||||
|
{item.namaLengkap}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text truncate fz="sm">
|
<Text truncate fz="md" fw={500} lh={1.45}>
|
||||||
|
{item.alamatKTP}
|
||||||
|
</Text>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Text truncate fz="md" fw={500} lh={1.45}>
|
||||||
{item.tanggalLahir ? new Date(item.tanggalLahir).toLocaleDateString() : '-'}
|
{item.tanggalLahir ? new Date(item.tanggalLahir).toLocaleDateString() : '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text truncate fz="sm">{item.jenisKelamin}</Text>
|
<Text truncate fz="md" fw={500} lh={1.45}>
|
||||||
|
{item.jenisKelamin}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
@@ -107,6 +129,9 @@ function ListBeasiswaPendaftar({ search }: { search: string }) {
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push(`/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar/${item.id}`)
|
router.push(`/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar/${item.id}`)
|
||||||
}
|
}
|
||||||
|
fz="sm"
|
||||||
|
fw={500}
|
||||||
|
lh={1.4}
|
||||||
>
|
>
|
||||||
<IconDeviceImacCog size={20} />
|
<IconDeviceImacCog size={20} />
|
||||||
<Text ml={5}>Detail</Text>
|
<Text ml={5}>Detail</Text>
|
||||||
@@ -117,8 +142,10 @@ function ListBeasiswaPendaftar({ search }: { search: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={6}>
|
<TableTd colSpan={6}>
|
||||||
<Center py={20}>
|
<Center py={{ base: 'sm', md: 'md' }}>
|
||||||
<Text c="dimmed">Tidak ada data pendaftar yang cocok</Text>
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data pendaftar yang cocok
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -126,6 +153,82 @@ function ListBeasiswaPendaftar({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile: Card */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="xs">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item, index) => (
|
||||||
|
<Paper key={item.id} withBorder p="sm" radius="sm">
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
No
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{index + 1}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Lengkap
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} truncate>
|
||||||
|
{item.namaLengkap}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Alamat
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} truncate>
|
||||||
|
{item.alamatKTP}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Tanggal Lahir
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.tanggalLahir ? new Date(item.tanggalLahir).toLocaleDateString() : '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Jenis Kelamin
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.jenisKelamin}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(`/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar/${item.id}`)
|
||||||
|
}
|
||||||
|
fullWidth
|
||||||
|
fz="sm"
|
||||||
|
fw={500}
|
||||||
|
lh={1.4}
|
||||||
|
>
|
||||||
|
<IconDeviceImacCog size={20} />
|
||||||
|
<Text ml={5}>Detail</Text>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py="sm">
|
||||||
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data pendaftar yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ function EditProgramKreatifDesa() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ function CreateKeunggulanProgram() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Title
|
Title
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit, IconPlus, IconSearch, IconTrashFilled } from '@tabler/icons-react';
|
import { IconEdit, IconPlus, IconSearch, IconTrashFilled } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -48,12 +48,13 @@ function ListKeunggulanProgram({ search }: { search: string }) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [modalHapus, setModalHapus] = useState(false);
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const { data, page, totalPages, loading, load } = stateList.findMany;
|
const { data, page, totalPages, loading, load } = stateList.findMany;
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10, search);
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search]);
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const handleHapus = () => {
|
const handleHapus = () => {
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
@@ -67,14 +68,14 @@ function ListKeunggulanProgram({ search }: { search: string }) {
|
|||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py="md">
|
||||||
<Skeleton height={600} radius="md" />
|
<Skeleton height={600} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py="md">
|
||||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb="md">
|
||||||
<Title order={4}>Daftar Keunggulan Program</Title>
|
<Title order={4}>Daftar Keunggulan Program</Title>
|
||||||
@@ -92,34 +93,44 @@ function ListKeunggulanProgram({ search }: { search: string }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
{/* Desktop Table */}
|
||||||
<Table highlightOnHover>
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ minWidth: 200 }}>Nama Keunggulan Program</TableTh>
|
<TableTh style={{ width: '30%' }}>Nama Keunggulan Program</TableTh>
|
||||||
<TableTh style={{ minWidth: 200 }}>Deskripsi</TableTh>
|
<TableTh style={{ width: '40%' }}>Deskripsi</TableTh>
|
||||||
<TableTh style={{ minWidth: 200 }}>Edit</TableTh>
|
<TableTh style={{ width: '15%' }}>Edit</TableTh>
|
||||||
<TableTh style={{ minWidth: 200 }}>Delete</TableTh>
|
<TableTh style={{ width: '15%' }}>Delete</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{filteredData.length > 0 ? (
|
{filteredData.length > 0 ? (
|
||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd style={{ minWidth: 200 }}>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>
|
<Text fz="md" fw={500} lh={1.45} truncate="end" lineClamp={1}>
|
||||||
{item.judul}
|
{item.judul}
|
||||||
</Text>
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ minWidth: 200 }}>
|
<TableTd>
|
||||||
<Text
|
<Text
|
||||||
|
fz="md"
|
||||||
fw={500}
|
fw={500}
|
||||||
|
lh={1.45}
|
||||||
truncate="end"
|
truncate="end"
|
||||||
lineClamp={1}
|
lineClamp={1}
|
||||||
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
||||||
/>
|
/>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ minWidth: 200 }}>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="green"
|
||||||
@@ -133,7 +144,7 @@ function ListKeunggulanProgram({ search }: { search: string }) {
|
|||||||
<IconEdit size={18} />
|
<IconEdit size={18} />
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ minWidth: 200 }}>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="red"
|
color="red"
|
||||||
@@ -151,8 +162,8 @@ function ListKeunggulanProgram({ search }: { search: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={4}>
|
<TableTd colSpan={4}>
|
||||||
<Center py={20}>
|
<Center py="xl">
|
||||||
<Text c="dimmed">
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
Tidak ada data keunggulan program yang cocok
|
Tidak ada data keunggulan program yang cocok
|
||||||
</Text>
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
@@ -162,9 +173,70 @@ function ListKeunggulanProgram({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Cards */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="sm">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
|
<Paper key={item.id} withBorder p="md" radius="sm">
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>Nama Keunggulan Program</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} truncate="end">
|
||||||
|
{item.judul}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>Deskripsi</Text>
|
||||||
|
<Text
|
||||||
|
fz="sm"
|
||||||
|
fw={500}
|
||||||
|
lh={1.4}
|
||||||
|
truncate="end"
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group justify="flex-end" mt="xs">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="green"
|
||||||
|
size="xs"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/pendidikan/beasiswa-desa/keunggulan-program/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconEdit size={16} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="red"
|
||||||
|
size="xs"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(item.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconTrashFilled size={16} />
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py="xl">
|
||||||
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data keunggulan program yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{totalPages > 1 && (
|
|
||||||
<Center>
|
<Center>
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
@@ -179,9 +251,7 @@ function ListKeunggulanProgram({ search }: { search: string }) {
|
|||||||
radius="md"
|
radius="md"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Modal Konfirmasi Hapus */}
|
|
||||||
<ModalKonfirmasiHapus
|
<ModalKonfirmasiHapus
|
||||||
opened={modalHapus}
|
opened={modalHapus}
|
||||||
onClose={() => setModalHapus(false)}
|
onClose={() => setModalHapus(false)}
|
||||||
|
|||||||
@@ -1,8 +1,29 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import LayoutTabs from './_lib/layoutTabs';
|
import LayoutTabs from './_lib/layoutTabs';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
import { Box } from '@mantine/core';
|
||||||
|
|
||||||
function Layout({ children }: { children: React.ReactNode }) {
|
function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Contoh path:
|
||||||
|
// - /darmasaba/desa/berita/semua → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
|
||||||
|
|
||||||
|
const segments = pathname.split('/').filter(Boolean);
|
||||||
|
const isDetailPage = segments.length >= 5;
|
||||||
|
|
||||||
|
if (isDetailPage) {
|
||||||
|
// Tampilkan tanpa tab menu
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutTabs>
|
<LayoutTabs>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
import { IconBuildingCommunity, IconCalendar, IconSchool } from '@tabler/icons-react';
|
import { IconBuildingCommunity, IconCalendar, IconSchool } from '@tabler/icons-react';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
@@ -62,6 +62,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
|
<Box visibleFrom='md' pb={10}>
|
||||||
<ScrollArea type="auto" offsetScrollbars>
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
<TabsList
|
<TabsList
|
||||||
p="sm"
|
p="sm"
|
||||||
@@ -84,6 +85,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
@@ -91,6 +93,45 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box hiddenFrom='md' pb={10}>
|
||||||
|
<ScrollArea
|
||||||
|
type="auto"
|
||||||
|
offsetScrollbars={false}
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TabsList
|
||||||
|
p="xs" // lebih kecil
|
||||||
|
style={{
|
||||||
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
gap: "0.5rem",
|
||||||
|
width: "max-content", // ⬅️ kunci
|
||||||
|
maxWidth: "100%", // ⬅️ penting
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<TabsTab
|
||||||
|
key={i}
|
||||||
|
value={tab.value}
|
||||||
|
leftSection={tab.icon}
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
paddingInline: "0.75rem", // ⬅️ lebih ramping
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ function EditFasilitasYangDisediakan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Divider, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Divider, Group, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -29,13 +29,10 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Grid align="center">
|
<Group justify='space-between' align="center">
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} c={colors['blue-button']}>
|
<Title order={3} c={colors['blue-button']}>
|
||||||
Pratinjau Fasilitas Yang Disediakan
|
Pratinjau Fasilitas Yang Disediakan
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }}>
|
|
||||||
<Button
|
<Button
|
||||||
c="green"
|
c="green"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -49,8 +46,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Konten Preview */}
|
{/* Konten Preview */}
|
||||||
<Paper
|
<Paper
|
||||||
@@ -59,6 +55,7 @@ function Page() {
|
|||||||
withBorder
|
withBorder
|
||||||
radius="md"
|
radius="md"
|
||||||
shadow="xs"
|
shadow="xs"
|
||||||
|
visibleFrom='md'
|
||||||
>
|
>
|
||||||
<Box px={{ base: 'sm', md: 50 }}>
|
<Box px={{ base: 'sm', md: 50 }}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
@@ -84,6 +81,47 @@ function Page() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
<Paper
|
||||||
|
p="xl"
|
||||||
|
bg={colors['white-1']}
|
||||||
|
withBorder
|
||||||
|
radius="md"
|
||||||
|
shadow="xs"
|
||||||
|
hiddenFrom='md'
|
||||||
|
>
|
||||||
|
<Box px={{ base: 'sm', md: 50 }}>
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Judul */}
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: '1.3rem', md: '1.8rem' }}
|
||||||
|
fw="bold"
|
||||||
|
c={colors['blue-button']}
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.judul }}
|
||||||
|
/>
|
||||||
|
<Divider my="md" color={colors['blue-button']} />
|
||||||
|
|
||||||
|
{/* Deskripsi */}
|
||||||
|
<Text
|
||||||
|
fz={{ base: '0.95rem', md: '1.2rem' }}
|
||||||
|
lh={1.7}
|
||||||
|
ta={{ base: 'left', md: 'justify' }}
|
||||||
|
c="dimmed"
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textWrap: 'pretty',
|
||||||
|
hyphens: 'auto',
|
||||||
|
'& p': {
|
||||||
|
marginBottom: '0.8em',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,29 @@
|
|||||||
|
'use client'
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import LayoutTabs from './_lib/layoutTabs';
|
import LayoutTabs from './_lib/layoutTabs';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
import { Box } from '@mantine/core';
|
||||||
|
|
||||||
function Layout({ children }: { children: React.ReactNode }) {
|
function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Contoh path:
|
||||||
|
// - /darmasaba/desa/berita/semua → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
|
||||||
|
|
||||||
|
const segments = pathname.split('/').filter(Boolean);
|
||||||
|
const isDetailPage = segments.length >= 5;
|
||||||
|
|
||||||
|
if (isDetailPage) {
|
||||||
|
// Tampilkan tanpa tab menu
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutTabs>
|
<LayoutTabs>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ function EditLokasiDanJadwal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Divider, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Divider, Group, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -29,13 +29,10 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Grid align="center">
|
<Group justify='space-between' align="center">
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} c={colors['blue-button']}>
|
<Title order={3} c={colors['blue-button']}>
|
||||||
Pratinjau Lokasi & Jadwal
|
Pratinjau Lokasi & Jadwal
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }}>
|
|
||||||
<Button
|
<Button
|
||||||
c="green"
|
c="green"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -49,8 +46,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Konten Preview */}
|
{/* Konten Preview */}
|
||||||
<Paper
|
<Paper
|
||||||
@@ -59,6 +55,7 @@ function Page() {
|
|||||||
withBorder
|
withBorder
|
||||||
radius="md"
|
radius="md"
|
||||||
shadow="xs"
|
shadow="xs"
|
||||||
|
visibleFrom='md'
|
||||||
>
|
>
|
||||||
<Box px={{ base: 'sm', md: 50 }}>
|
<Box px={{ base: 'sm', md: 50 }}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
@@ -85,6 +82,48 @@ function Page() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
<Paper
|
||||||
|
p="xl"
|
||||||
|
bg={colors['white-1']}
|
||||||
|
withBorder
|
||||||
|
radius="md"
|
||||||
|
shadow="xs"
|
||||||
|
hiddenFrom='md'
|
||||||
|
>
|
||||||
|
<Box px={{ base: 'sm', md: 50 }}>
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Judul */}
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: '1.3rem', md: '1.8rem' }}
|
||||||
|
fw="bold"
|
||||||
|
c={colors['blue-button']}
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.judul }}
|
||||||
|
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
||||||
|
/>
|
||||||
|
<Divider my="md" color={colors['blue-button']} />
|
||||||
|
|
||||||
|
{/* Deskripsi */}
|
||||||
|
<Text
|
||||||
|
fz={{ base: '0.95rem', md: '1.2rem' }}
|
||||||
|
lh={1.7}
|
||||||
|
ta={{ base: 'left', md: 'justify' }}
|
||||||
|
c="dimmed"
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textWrap: 'pretty',
|
||||||
|
hyphens: 'auto',
|
||||||
|
'& p': {
|
||||||
|
marginBottom: '0.8em',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ function EditTujuanProgram() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={handleBack} p="xs" radius="md">
|
<Button variant="subtle" onClick={handleBack} p="xs" radius="md">
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
Grid,
|
Group,
|
||||||
GridCol,
|
|
||||||
Paper,
|
Paper,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
@@ -40,13 +39,10 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Grid align="center">
|
<Group justify='space-between' align="center">
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} c={colors['blue-button']}>
|
<Title order={3} c={colors['blue-button']}>
|
||||||
Pratinjau Tujuan Program
|
Pratinjau Tujuan Program
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }}>
|
|
||||||
<Button
|
<Button
|
||||||
c="green"
|
c="green"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -60,8 +56,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Konten Preview */}
|
{/* Konten Preview */}
|
||||||
<Paper
|
<Paper
|
||||||
@@ -70,6 +65,7 @@ function Page() {
|
|||||||
withBorder
|
withBorder
|
||||||
radius="md"
|
radius="md"
|
||||||
shadow="xs"
|
shadow="xs"
|
||||||
|
visibleFrom='md'
|
||||||
>
|
>
|
||||||
<Box px={{ base: 'sm', md: 50 }}>
|
<Box px={{ base: 'sm', md: 50 }}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
@@ -95,6 +91,47 @@ function Page() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
<Paper
|
||||||
|
p="xl"
|
||||||
|
bg={colors['white-1']}
|
||||||
|
withBorder
|
||||||
|
radius="md"
|
||||||
|
shadow="xs"
|
||||||
|
hiddenFrom='md'
|
||||||
|
>
|
||||||
|
<Box px={{ base: 'sm', md: 50 }}>
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Judul */}
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: '1.3rem', md: '1.8rem' }}
|
||||||
|
fw="bold"
|
||||||
|
c={colors['blue-button']}
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.judul }}
|
||||||
|
/>
|
||||||
|
<Divider my="md" color={colors['blue-button']} />
|
||||||
|
|
||||||
|
{/* Deskripsi */}
|
||||||
|
<Text
|
||||||
|
fz={{ base: '0.95rem', md: '1.2rem' }}
|
||||||
|
lh={1.7}
|
||||||
|
ta={{ base: 'left', md: 'justify' }}
|
||||||
|
c="dimmed"
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textWrap: 'pretty',
|
||||||
|
hyphens: 'auto',
|
||||||
|
'& p': {
|
||||||
|
marginBottom: '0.8em',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export default function EditDataPendidikan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'xl' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md" gap="sm">
|
<Group mb="md" gap="sm">
|
||||||
<Button variant="subtle" onClick={() => router.back()} radius="md">
|
<Button variant="subtle" onClick={() => router.back()} radius="md">
|
||||||
<IconArrowBack size={20} stroke={2} />
|
<IconArrowBack size={20} stroke={2} />
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default function CreateDataPendidikan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'xl' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md" gap="sm">
|
<Group mb="md" gap="sm">
|
||||||
<Button variant="subtle" onClick={() => router.back()} radius="md">
|
<Button variant="subtle" onClick={() => router.back()} radius="md">
|
||||||
<IconArrowBack size={20} stroke={2} />
|
<IconArrowBack size={20} stroke={2} />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
import { IconBuilding, IconChalkboard, IconMicroscope, IconSchool } from '@tabler/icons-react';
|
import { IconBuilding, IconChalkboard, IconMicroscope, IconSchool } from '@tabler/icons-react';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
@@ -68,6 +68,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
|
<Box visibleFrom='md' pb={10}>
|
||||||
<ScrollArea type="auto" offsetScrollbars>
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
<TabsList
|
<TabsList
|
||||||
p="sm"
|
p="sm"
|
||||||
@@ -90,6 +91,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
@@ -97,6 +99,45 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box hiddenFrom='md' pb={10}>
|
||||||
|
<ScrollArea
|
||||||
|
type="auto"
|
||||||
|
offsetScrollbars={false}
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TabsList
|
||||||
|
p="xs" // lebih kecil
|
||||||
|
style={{
|
||||||
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
gap: "0.5rem",
|
||||||
|
width: "max-content", // ⬅️ kunci
|
||||||
|
maxWidth: "100%", // ⬅️ penting
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<TabsTab
|
||||||
|
key={i}
|
||||||
|
value={tab.value}
|
||||||
|
leftSection={tab.icon}
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
paddingInline: "0.75rem", // ⬅️ lebih ramping
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ function EditJenjangPendidikan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ function CreateJenjangPendidikan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Back button + Title */}
|
{/* Back button + Title */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,6 +1,23 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
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 { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit, IconPlus, IconX } from '@tabler/icons-react';
|
import { IconEdit, IconPlus, IconX } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -9,29 +26,25 @@ import { useProxy } from 'valtio/utils';
|
|||||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud';
|
import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud';
|
||||||
|
|
||||||
|
|
||||||
function JenjangPendidikan() {
|
function JenjangPendidikan() {
|
||||||
// const [search, setSearch] = useState("")
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Title order={4}>Jenjang Pendidikan</Title>
|
<Title order={2} visibleFrom="md">
|
||||||
{/* <HeaderSearch
|
Jenjang Pendidikan
|
||||||
title='Jenjang Pendidikan'
|
</Title>
|
||||||
placeholder='Cari jenjang pendidikan...'
|
<Title order={2} hiddenFrom="md">
|
||||||
searchIcon={<IconSearch size={20} />}
|
Jenjang Pendidikan
|
||||||
value={search}
|
</Title>
|
||||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
|
||||||
/> */}
|
|
||||||
<ListJenjangPendidikan />
|
<ListJenjangPendidikan />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ListJenjangPendidikan() {
|
function ListJenjangPendidikan() {
|
||||||
const stateList = useProxy(infoSekolahPaud.jenjangPendidikan)
|
const stateList = useProxy(infoSekolahPaud.jenjangPendidikan);
|
||||||
const [modalHapus, setModalHapus] = useState(false)
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -39,50 +52,65 @@ function ListJenjangPendidikan() {
|
|||||||
totalPages,
|
totalPages,
|
||||||
loading,
|
loading,
|
||||||
load,
|
load,
|
||||||
} = stateList.findMany
|
} = stateList.findMany;
|
||||||
|
|
||||||
const handleHapus = () => {
|
const handleHapus = () => {
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
stateList.delete.byId(selectedId)
|
stateList.delete.byId(selectedId);
|
||||||
setModalHapus(false)
|
setModalHapus(false);
|
||||||
setSelectedId(null)
|
setSelectedId(null);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10)
|
load(page, 10);
|
||||||
}, [page])
|
}, [page]);
|
||||||
|
|
||||||
const filteredData = data || []
|
const filteredData = data || [];
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py="md">
|
||||||
<Skeleton h={500} radius="md" />
|
<Skeleton h={500} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py="md">
|
||||||
<Paper bg={colors['white-1']} p="lg" withBorder shadow="md" radius="md">
|
<Paper bg={colors['white-1']} p="lg" withBorder shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb="md">
|
||||||
<Title order={4}>Daftar Jenjang Pendidikan</Title>
|
<Title order={3} visibleFrom="md">
|
||||||
|
Daftar Jenjang Pendidikan
|
||||||
|
</Title>
|
||||||
|
<Title order={3} hiddenFrom="md">
|
||||||
|
Daftar Jenjang Pendidikan
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => router.push('/admin/pendidikan/info-sekolah/jenjang-pendidikan/create')}
|
onClick={() =>
|
||||||
|
router.push('/admin/pendidikan/info-sekolah/jenjang-pendidikan/create')
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box style={{ overflowX: "auto" }}>
|
{/* Desktop Table */}
|
||||||
<Table highlightOnHover>
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>Nama Jenjang Pendidikan</TableTh>
|
<TableTh style={{ width: '70%' }}>Nama Jenjang Pendidikan</TableTh>
|
||||||
<TableTh style={{ width: '15%' }}>Edit</TableTh>
|
<TableTh style={{ width: '15%' }}>Edit</TableTh>
|
||||||
<TableTh style={{ width: '15%' }}>Delete</TableTh>
|
<TableTh style={{ width: '15%' }}>Delete</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -92,13 +120,19 @@ function ListJenjangPendidikan() {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end">{item.nama}</Text>
|
<Text fz="md" fw={500} lh={1.45} truncate="end">
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="green"
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => router.push(`/admin/pendidikan/info-sekolah/jenjang-pendidikan/${item.id}`)}
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/pendidikan/info-sekolah/jenjang-pendidikan/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<IconEdit size={20} />
|
<IconEdit size={20} />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -108,8 +142,8 @@ function ListJenjangPendidikan() {
|
|||||||
color="red"
|
color="red"
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedId(item.id)
|
setSelectedId(item.id);
|
||||||
setModalHapus(true)
|
setModalHapus(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconX size={20} />
|
<IconX size={20} />
|
||||||
@@ -120,8 +154,10 @@ function ListJenjangPendidikan() {
|
|||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={3}>
|
<TableTd colSpan={3}>
|
||||||
<Center py={20}>
|
<Center py="xl">
|
||||||
<Text color="dimmed">Tidak ada data jenjang pendidikan</Text>
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data jenjang pendidikan
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -129,6 +165,55 @@ function ListJenjangPendidikan() {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Cards */}
|
||||||
|
<Stack hiddenFrom="md" gap="sm">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
|
<Paper key={item.id} withBorder p="sm" radius="md">
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Jenjang Pendidikan
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Group mt="sm" justify="flex-end" gap="xs">
|
||||||
|
<Button
|
||||||
|
color="green"
|
||||||
|
variant="light"
|
||||||
|
size="compact-sm"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/pendidikan/info-sekolah/jenjang-pendidikan/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconEdit size={16} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
variant="light"
|
||||||
|
size="compact-sm"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(item.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconX size={16} />
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py="xl">
|
||||||
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data jenjang pendidikan
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
@@ -151,7 +236,7 @@ function ListJenjangPendidikan() {
|
|||||||
opened={modalHapus}
|
opened={modalHapus}
|
||||||
onClose={() => setModalHapus(false)}
|
onClose={() => setModalHapus(false)}
|
||||||
onConfirm={handleHapus}
|
onConfirm={handleHapus}
|
||||||
text='Apakah anda yakin ingin menghapus jenjang pendidikan ini?'
|
text="Apakah anda yakin ingin menghapus jenjang pendidikan ini?"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,29 @@
|
|||||||
|
'use client'
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import LayoutTabs from './_lib/layoutTabs';
|
import LayoutTabs from './_lib/layoutTabs';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
import { Box } from '@mantine/core';
|
||||||
|
|
||||||
function Layout({ children }: { children: React.ReactNode }) {
|
function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Contoh path:
|
||||||
|
// - /darmasaba/desa/berita/semua → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
|
||||||
|
|
||||||
|
const segments = pathname.split('/').filter(Boolean);
|
||||||
|
const isDetailPage = segments.length >= 5;
|
||||||
|
|
||||||
|
if (isDetailPage) {
|
||||||
|
// Tampilkan tanpa tab menu
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutTabs>
|
<LayoutTabs>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export default function EditLembaga() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function DetailLembaga() {
|
|||||||
const data = detailState.findUnique.data;
|
const data = detailState.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
onClick={() => router.back()}
|
onClick={() => router.back()}
|
||||||
@@ -53,7 +53,7 @@ function DetailLembaga() {
|
|||||||
|
|
||||||
<Paper
|
<Paper
|
||||||
withBorder
|
withBorder
|
||||||
w={{ base: "100%", md: "60%" }}
|
w={{ base: "100%", md: "70%" }}
|
||||||
bg={colors['white-1']}
|
bg={colors['white-1']}
|
||||||
p="lg"
|
p="lg"
|
||||||
radius="md"
|
radius="md"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ function CreateLembaga() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Title
|
Title
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -27,7 +27,7 @@ import HeaderSearch from '../../../_com/header';
|
|||||||
import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud';
|
import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud';
|
||||||
|
|
||||||
function Lembaga() {
|
function Lembaga() {
|
||||||
const [search, setSearch] = useState("")
|
const [search, setSearch] = useState("");
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<HeaderSearch
|
<HeaderSearch
|
||||||
@@ -43,8 +43,9 @@ function Lembaga() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListLembaga({ search }: { search: string }) {
|
function ListLembaga({ search }: { search: string }) {
|
||||||
const stateList = useProxy(infoSekolahPaud.lembagaPendidikan)
|
const stateList = useProxy(infoSekolahPaud.lembagaPendidikan);
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -55,40 +56,65 @@ function ListLembaga({ search }: { search: string }) {
|
|||||||
} = stateList.findMany;
|
} = stateList.findMany;
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10, search)
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search])
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || []
|
const filteredData = data || [];
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={{ base: 'sm', md: 'md' }}>
|
||||||
<Skeleton height={600} radius="md" />
|
<Skeleton height={600} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={{ base: 'sm', md: 'md' }}>
|
||||||
<Paper withBorder bg={colors['white-1']} p={'lg'} shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p={{ base: 'sm', md: 'lg' }} shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||||
<Title order={4}>Daftar Lembaga</Title>
|
<Title order={4} lh={1.2}>
|
||||||
|
Daftar Lembaga
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => router.push('/admin/pendidikan/info-sekolah/lembaga/create')}
|
onClick={() =>
|
||||||
|
router.push('/admin/pendidikan/info-sekolah/lembaga/create')
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
<Box style={{ overflowX: "auto" }}>
|
|
||||||
<Table highlightOnHover>
|
{/* Desktop Table */}
|
||||||
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '40%' }}>Nama Lembaga</TableTh>
|
<TableTh style={{ width: '40%' }}>
|
||||||
<TableTh style={{ width: '30%' }}>Jenjang Pendidikan</TableTh>
|
<Text fz="sm" fw={600} lh={1.4} ta="left">
|
||||||
<TableTh style={{ width: '15%' }}>Aksi</TableTh>
|
Nama Lembaga
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '30%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} ta="left">
|
||||||
|
Jenjang Pendidikan
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '15%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} ta="left">
|
||||||
|
Aksi
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
@@ -96,19 +122,29 @@ function ListLembaga({ search }: { search: string }) {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>{item.nama}</Text>
|
<Text fz="md" fw={500} lh={1.5} truncate="end">
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fz="sm" c="dimmed">{item.jenjangPendidikan?.nama || '-'}</Text>
|
<Text fz="sm" fw={500} lh={1.5} c="gray.7">
|
||||||
|
{item.jenjangPendidikan?.nama || '-'}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
onClick={() => router.push(`/admin/pendidikan/info-sekolah/lembaga/${item.id}`)}
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/pendidikan/info-sekolah/lembaga/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<IconDeviceImac size={20} />
|
<IconDeviceImac size={20} />
|
||||||
<Text ml={5}>Detail</Text>
|
<Text ml={5} fz="sm" fw={500} lh={1.4}>
|
||||||
|
Detail
|
||||||
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -116,8 +152,10 @@ function ListLembaga({ search }: { search: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={3}>
|
<TableTd colSpan={3}>
|
||||||
<Center py={20}>
|
<Center py={{ base: 20, md: 24 }}>
|
||||||
<Text color="dimmed">Tidak ada data lembaga yang cocok</Text>
|
<Text c="gray.6" fz="sm" lh={1.5}>
|
||||||
|
Tidak ada data lembaga yang cocok
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -125,6 +163,59 @@ function ListLembaga({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Card View */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="xs">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
|
<Paper key={item.id} p="sm" withBorder bg="white">
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} c="gray.7">
|
||||||
|
Nama Lembaga
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} c="gray.7">
|
||||||
|
Jenjang Pendidikan
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} c="gray.7">
|
||||||
|
{item.jenjangPendidikan?.nama || '-'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/pendidikan/info-sekolah/lembaga/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconDeviceImac size={18} />
|
||||||
|
<Text ml={6} fz="sm" fw={500} lh={1.4}>
|
||||||
|
Detail
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py={20}>
|
||||||
|
<Text c="gray.6" fz="sm" lh={1.5}>
|
||||||
|
Tidak ada data lembaga yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ function EditPengajar() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header Back + Title */}
|
{/* Header Back + Title */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function DetailPengajar() {
|
|||||||
const data = detailState.findUnique.data;
|
const data = detailState.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
onClick={() => router.back()}
|
onClick={() => router.back()}
|
||||||
@@ -53,7 +53,7 @@ function DetailPengajar() {
|
|||||||
|
|
||||||
<Paper
|
<Paper
|
||||||
withBorder
|
withBorder
|
||||||
w={{ base: "100%", md: "60%" }}
|
w={{ base: "100%", md: "70%" }}
|
||||||
bg={colors['white-1']}
|
bg={colors['white-1']}
|
||||||
p="lg"
|
p="lg"
|
||||||
radius="md"
|
radius="md"
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ function CreatePengajar() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header Back + Title */}
|
{/* Header Back + Title */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import HeaderSearch from '../../../_com/header';
|
import HeaderSearch from '../../../_com/header';
|
||||||
import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud';
|
import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
|
|
||||||
function Pengajar() {
|
function Pengajar() {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
@@ -29,8 +30,9 @@ function Pengajar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListPengajar({ search }: { search: string }) {
|
function ListPengajar({ search }: { search: string }) {
|
||||||
const listState = useProxy(infoSekolahPaud.pengajar)
|
const listState = useProxy(infoSekolahPaud.pengajar);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -41,24 +43,26 @@ function ListPengajar({ search }: { search: string }) {
|
|||||||
} = listState.findMany;
|
} = listState.findMany;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load(page, 10, search)
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search])
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || []
|
const filteredData = data || [];
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={{ base: 'sm', md: 'md' }}>
|
||||||
<Skeleton h={500} radius="md" />
|
<Skeleton h={500} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={{ base: 'sm', md: 'md' }}>
|
||||||
<Paper withBorder bg={colors['white-1']} p={'lg'} shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p={{ base: 'sm', md: 'lg' }} shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||||
<Title order={4}>Daftar Pengajar</Title>
|
<Title order={4} lh={1.2}>
|
||||||
|
Daftar Pengajar
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
@@ -69,13 +73,33 @@ function ListPengajar({ search }: { search: string }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
{/* Desktop Table */}
|
||||||
<Table highlightOnHover striped withRowBorders style={{ minWidth: '700px' }}>
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '35%' }}>Nama Pengajar</TableTh>
|
<TableTh style={{ width: '35%' }}>
|
||||||
<TableTh style={{ width: '35%' }}>Lembaga</TableTh>
|
<Text fz="sm" fw={600} lh={1.4} ta="left">
|
||||||
<TableTh style={{ width: '20%' }}>Aksi</TableTh>
|
Nama Pengajar
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '35%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} ta="left">
|
||||||
|
Lembaga
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '30%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4} ta="left">
|
||||||
|
Aksi
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
@@ -83,10 +107,14 @@ function ListPengajar({ search }: { search: string }) {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end" lineClamp={1}>{item.nama}</Text>
|
<Text fz="md" fw={500} lh={1.5} truncate="end" lineClamp={1}>
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text truncate="end" fz="sm" c="dimmed">{item.lembaga.nama}</Text>
|
<Text fz="md" fw={500} lh={1.5} c="gray.7" truncate="end">
|
||||||
|
{item.lembaga.nama}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
@@ -95,7 +123,9 @@ function ListPengajar({ search }: { search: string }) {
|
|||||||
onClick={() => router.push(`/admin/pendidikan/info-sekolah/pengajar/${item.id}`)}
|
onClick={() => router.push(`/admin/pendidikan/info-sekolah/pengajar/${item.id}`)}
|
||||||
>
|
>
|
||||||
<IconDeviceImacCog size={20} />
|
<IconDeviceImacCog size={20} />
|
||||||
<Text ml={5}>Detail</Text>
|
<Text ml={5} fz="sm" fw={500} lh={1.4}>
|
||||||
|
Detail
|
||||||
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -103,8 +133,10 @@ function ListPengajar({ search }: { search: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={3}>
|
<TableTd colSpan={3}>
|
||||||
<Center py={20}>
|
<Center py={{ base: 'md', md: 'lg' }}>
|
||||||
<Text c="dimmed">Tidak ada data pengajar yang cocok</Text>
|
<Text c="gray.6" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data pengajar yang cocok
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -112,14 +144,63 @@ function ListPengajar({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Card View */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="xs">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
|
<Paper key={item.id} withBorder radius="md" p="sm">
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Pengajar
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Lembaga
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} c="gray.7">
|
||||||
|
{item.lembaga.nama}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
onClick={() => router.push(`/admin/pendidikan/info-sekolah/pengajar/${item.id}`)}
|
||||||
|
>
|
||||||
|
<IconDeviceImacCog size={18} />
|
||||||
|
<Text ml={5} fz="sm" fw={500} lh={1.4}>
|
||||||
|
Detail
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py="md">
|
||||||
|
<Text c="gray.6" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data pengajar yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
onChange={(newPage) => {
|
onChange={(newPage) => {
|
||||||
load(newPage, 10, search)
|
load(newPage, 10, search);
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
}}
|
}}
|
||||||
total={totalPages}
|
total={totalPages}
|
||||||
mt="md"
|
mt="md"
|
||||||
@@ -129,7 +210,7 @@ function ListPengajar({ search }: { search: string }) {
|
|||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Pengajar;
|
export default Pengajar;
|
||||||
@@ -107,7 +107,7 @@ function EditSiswa() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ function DetailSiswa() {
|
|||||||
const data = detailState.findUnique.data
|
const data = detailState.findUnique.data
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Tombol Kembali */}
|
{/* Tombol Kembali */}
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
@@ -55,7 +55,7 @@ function DetailSiswa() {
|
|||||||
{/* Card Utama */}
|
{/* Card Utama */}
|
||||||
<Paper
|
<Paper
|
||||||
withBorder
|
withBorder
|
||||||
w={{ base: "100%", md: "60%" }}
|
w={{ base: "100%", md: "70%" }}
|
||||||
bg={colors['white-1']}
|
bg={colors['white-1']}
|
||||||
p="lg"
|
p="lg"
|
||||||
radius="md"
|
radius="md"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ function CreateSiswa() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header + Back Button */}
|
{/* Header + Back Button */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
TableThead,
|
TableThead,
|
||||||
TableTr,
|
TableTr,
|
||||||
Text,
|
Text,
|
||||||
Title
|
Title,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -25,6 +25,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import HeaderSearch from '../../../_com/header';
|
import HeaderSearch from '../../../_com/header';
|
||||||
import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud';
|
import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
|
|
||||||
function Siswa() {
|
function Siswa() {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
@@ -43,30 +44,33 @@ function Siswa() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListSiswa({ search }: { search: string }) {
|
function ListSiswa({ search }: { search: string }) {
|
||||||
const stateSiswa = useProxy(infoSekolahPaud.siswa)
|
const stateSiswa = useProxy(infoSekolahPaud.siswa);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const { data, page, totalPages, loading, load } = stateSiswa.findMany;
|
const { data, page, totalPages, loading, load } = stateSiswa.findMany;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load(page, 10, search)
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search])
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || []
|
const filteredData = data || [];
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={{ base: 'sm', md: 'md' }}>
|
||||||
<Skeleton height={500} radius="md" />
|
<Skeleton height={500} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={{ base: 'sm', md: 'md' }}>
|
||||||
<Paper withBorder bg={colors['white-1']} p={'lg'} shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||||
<Title order={4}>Daftar Siswa</Title>
|
<Title order={4} lh={1.2}>
|
||||||
|
Daftar Siswa
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
@@ -77,13 +81,35 @@ function ListSiswa({ search }: { search: string }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Box style={{ overflowX: "auto" }}>
|
{/* Desktop Table */}
|
||||||
<Table highlightOnHover striped withRowBorders style={{ minWidth: '700px' }}>
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
striped
|
||||||
|
withRowBorders
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '35%' }}>Nama Siswa</TableTh>
|
<TableTh style={{ width: '35%' }}>
|
||||||
<TableTh style={{ width: '35%' }}>Lembaga</TableTh>
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
<TableTh style={{ width: '20%' }}>Aksi</TableTh>
|
Nama Siswa
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '35%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Lembaga
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '20%' }}>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Aksi
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
@@ -91,19 +117,27 @@ function ListSiswa({ search }: { search: string }) {
|
|||||||
filteredData.map((item) => (
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text fw={500} truncate="end">{item.nama}</Text>
|
<Text fz="md" fw={500} lh={1.5} truncate="end">
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text truncate="end" c="dimmed">{item.lembaga.nama}</Text>
|
<Text fz="md" fw={500} lh={1.5} truncate="end">
|
||||||
|
{item.lembaga.nama}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
onClick={() => router.push(`/admin/pendidikan/info-sekolah/siswa/${item.id}`)}
|
onClick={() =>
|
||||||
|
router.push(`/admin/pendidikan/info-sekolah/siswa/${item.id}`)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<IconDeviceImacCog size={20} />
|
<IconDeviceImacCog size={20} />
|
||||||
<Text ml={5}>Detail</Text>
|
<Text ml={5} fz="sm" fw={500} lh={1.4}>
|
||||||
|
Detail
|
||||||
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -111,8 +145,10 @@ function ListSiswa({ search }: { search: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={3}>
|
<TableTd colSpan={3}>
|
||||||
<Center py={20}>
|
<Center py={{ base: 'sm', md: 'md' }}>
|
||||||
<Text color="dimmed">Tidak ada data siswa yang cocok</Text>
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data siswa yang cocok
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -120,7 +156,59 @@ function ListSiswa({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Cards */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="xs">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
|
<Paper key={item.id} withBorder p="sm" radius="md">
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Nama Siswa
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.nama}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>
|
||||||
|
Lembaga
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{item.lembaga.nama}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(`/admin/pendidikan/info-sekolah/siswa/${item.id}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconDeviceImacCog size={20} />
|
||||||
|
<Text ml={5} fz="sm" fw={500} lh={1.4}>
|
||||||
|
Detail
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py="sm">
|
||||||
|
<Text c="dimmed" fz="sm" lh={1.4}>
|
||||||
|
Tidak ada data siswa yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
<Pagination
|
<Pagination
|
||||||
@@ -130,14 +218,14 @@ function ListSiswa({ search }: { search: string }) {
|
|||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
}}
|
}}
|
||||||
total={totalPages}
|
total={totalPages}
|
||||||
mt="md"
|
mt={{ base: 'sm', md: 'md' }}
|
||||||
mb="md"
|
mb={{ base: 'sm', md: 'md' }}
|
||||||
color="blue"
|
color="blue"
|
||||||
radius="md"
|
radius="md"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Siswa;
|
export default Siswa;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
import { IconBook2, IconMapPin, IconSchool } from '@tabler/icons-react';
|
import { IconBook2, IconMapPin, IconSchool } from '@tabler/icons-react';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
@@ -63,6 +63,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
|
<Box visibleFrom='md' pb={10}>
|
||||||
<ScrollArea type="auto" offsetScrollbars>
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
<TabsList
|
<TabsList
|
||||||
p="sm"
|
p="sm"
|
||||||
@@ -85,6 +86,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
@@ -92,6 +94,45 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box hiddenFrom='md' pb={10}>
|
||||||
|
<ScrollArea
|
||||||
|
type="auto"
|
||||||
|
offsetScrollbars={false}
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TabsList
|
||||||
|
p="xs" // lebih kecil
|
||||||
|
style={{
|
||||||
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
gap: "0.5rem",
|
||||||
|
width: "max-content", // ⬅️ kunci
|
||||||
|
maxWidth: "100%", // ⬅️ penting
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<TabsTab
|
||||||
|
key={i}
|
||||||
|
value={tab.value}
|
||||||
|
leftSection={tab.icon}
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
paddingInline: "0.75rem", // ⬅️ lebih ramping
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ function EditJenisProgramYangDiselenggarakan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
{/* Back Button + Title */}
|
{/* Back Button + Title */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
Grid,
|
Group,
|
||||||
GridCol,
|
|
||||||
Paper,
|
Paper,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
@@ -40,13 +39,10 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Grid align="center">
|
<Group justify='space-between' align="center">
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} c={colors['blue-button']}>
|
<Title order={3} c={colors['blue-button']}>
|
||||||
Preview Jenis Program Yang Diselenggarakan
|
Preview Jenis Program Yang Diselenggarakan
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }}>
|
|
||||||
<Button
|
<Button
|
||||||
c="green"
|
c="green"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -60,8 +56,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Konten Preview */}
|
{/* Konten Preview */}
|
||||||
<Paper
|
<Paper
|
||||||
@@ -86,12 +81,20 @@ function Page() {
|
|||||||
|
|
||||||
{/* Deskripsi */}
|
{/* Deskripsi */}
|
||||||
<Text
|
<Text
|
||||||
fz={{ base: '1rem', md: '1.2rem' }}
|
fz={{ base: '0.95rem', md: '1.2rem' }}
|
||||||
lh={1.7}
|
lh={1.7}
|
||||||
ta="justify"
|
ta={{ base: 'left', md: 'justify' }}
|
||||||
c="dimmed"
|
c="dimmed"
|
||||||
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textWrap: 'pretty',
|
||||||
|
hyphens: 'auto',
|
||||||
|
'& p': {
|
||||||
|
marginBottom: '0.8em',
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,8 +1,30 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import LayoutTabs from './_lib/layoutTabs';
|
import LayoutTabs from './_lib/layoutTabs';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
import { Box } from '@mantine/core';
|
||||||
|
|
||||||
function Layout({ children }: { children: React.ReactNode }) {
|
function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Contoh path:
|
||||||
|
// - /darmasaba/desa/berita/semua → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
|
||||||
|
|
||||||
|
const segments = pathname.split('/').filter(Boolean);
|
||||||
|
const isDetailPage = segments.length >= 5;
|
||||||
|
|
||||||
|
if (isDetailPage) {
|
||||||
|
// Tampilkan tanpa tab menu
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutTabs>
|
<LayoutTabs>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ function EditTempatKegiatan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
{/* Back Button + Title */}
|
{/* Back Button + Title */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Divider, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Box, Button, Divider, Group, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit } from '@tabler/icons-react';
|
import { IconEdit } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -29,13 +29,10 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Grid align="center">
|
<Group justify='space-between' align="center">
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} c={colors['blue-button']}>
|
<Title order={3} c={colors['blue-button']}>
|
||||||
Preview Tempat Kegiatan
|
Preview Tempat Kegiatan
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }}>
|
|
||||||
<Button
|
<Button
|
||||||
c="green"
|
c="green"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -49,8 +46,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Konten Preview */}
|
{/* Konten Preview */}
|
||||||
<Paper
|
<Paper
|
||||||
@@ -75,12 +71,20 @@ function Page() {
|
|||||||
|
|
||||||
{/* Deskripsi */}
|
{/* Deskripsi */}
|
||||||
<Text
|
<Text
|
||||||
fz={{ base: '1rem', md: '1.2rem' }}
|
fz={{ base: '0.95rem', md: '1.2rem' }}
|
||||||
lh={1.7}
|
lh={1.7}
|
||||||
ta="justify"
|
ta={{ base: 'left', md: 'justify' }}
|
||||||
c="dimmed"
|
c="dimmed"
|
||||||
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textWrap: 'pretty',
|
||||||
|
hyphens: 'auto',
|
||||||
|
'& p': {
|
||||||
|
marginBottom: '0.8em',
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ function EditTujuanProgram() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={handleBack} p="xs" radius="md">
|
<Button variant="subtle" onClick={handleBack} p="xs" radius="md">
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
Grid,
|
Group,
|
||||||
GridCol,
|
|
||||||
Paper,
|
Paper,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
@@ -42,13 +41,10 @@ function Page() {
|
|||||||
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
<Paper bg={colors['white-1']} p="lg" radius="md" shadow="sm">
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Grid align="center">
|
<Group justify='space-between' align="center">
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
|
||||||
<Title order={3} c={colors['blue-button']}>
|
<Title order={3} c={colors['blue-button']}>
|
||||||
Pratinjau Pendidikan Non Formal
|
Pratinjau Pendidikan Non Formal
|
||||||
</Title>
|
</Title>
|
||||||
</GridCol>
|
|
||||||
<GridCol span={{ base: 12, md: 1 }}>
|
|
||||||
<Button
|
<Button
|
||||||
c="green"
|
c="green"
|
||||||
variant="light"
|
variant="light"
|
||||||
@@ -62,8 +58,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</Group>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Konten Preview */}
|
{/* Konten Preview */}
|
||||||
<Paper
|
<Paper
|
||||||
@@ -88,12 +83,20 @@ function Page() {
|
|||||||
|
|
||||||
{/* Deskripsi */}
|
{/* Deskripsi */}
|
||||||
<Text
|
<Text
|
||||||
fz={{ base: '1rem', md: '1.2rem' }}
|
fz={{ base: '0.95rem', md: '1.2rem' }}
|
||||||
lh={1.7}
|
lh={1.7}
|
||||||
ta="justify"
|
ta={{ base: 'left', md: 'justify' }}
|
||||||
c="dimmed"
|
c="dimmed"
|
||||||
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textWrap: 'pretty',
|
||||||
|
hyphens: 'auto',
|
||||||
|
'& p': {
|
||||||
|
marginBottom: '0.8em',
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
import { IconBook2, IconCategory, IconUser } from '@tabler/icons-react';
|
import { IconBook2, IconCategory, IconUser } from '@tabler/icons-react';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
@@ -62,6 +62,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
radius="lg"
|
radius="lg"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
>
|
>
|
||||||
|
<Box visibleFrom='md' pb={10}>
|
||||||
<ScrollArea type="auto" offsetScrollbars>
|
<ScrollArea type="auto" offsetScrollbars>
|
||||||
<TabsList
|
<TabsList
|
||||||
p="sm"
|
p="sm"
|
||||||
@@ -84,6 +85,7 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
@@ -91,6 +93,45 @@ function LayoutTabs({ children }: { children: React.ReactNode }) {
|
|||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box hiddenFrom='md' pb={10}>
|
||||||
|
<ScrollArea
|
||||||
|
type="auto"
|
||||||
|
offsetScrollbars={false}
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TabsList
|
||||||
|
p="xs" // lebih kecil
|
||||||
|
style={{
|
||||||
|
background: "linear-gradient(135deg, #e7ebf7, #f9faff)",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
gap: "0.5rem",
|
||||||
|
width: "max-content", // ⬅️ kunci
|
||||||
|
maxWidth: "100%", // ⬅️ penting
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, i) => (
|
||||||
|
<TabsTab
|
||||||
|
key={i}
|
||||||
|
value={tab.value}
|
||||||
|
leftSection={tab.icon}
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
paddingInline: "0.75rem", // ⬅️ lebih ramping
|
||||||
|
flexShrink: 0, // ✅ jangan mengecil aneh-aneh
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</TabsTab>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{tabs.map((tab, i) => (
|
{tabs.map((tab, i) => (
|
||||||
<TabsPanel
|
<TabsPanel
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ function EditPerpustakaanDigital() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function DetailDataPerpustakaan() {
|
|||||||
const data = stateDetail.findUnique.data;
|
const data = stateDetail.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
onClick={() => router.back()}
|
onClick={() => router.back()}
|
||||||
@@ -52,7 +52,7 @@ function DetailDataPerpustakaan() {
|
|||||||
|
|
||||||
<Paper
|
<Paper
|
||||||
withBorder
|
withBorder
|
||||||
w={{ base: "100%", md: "60%" }}
|
w={{ base: "100%", md: "70%" }}
|
||||||
bg={colors['white-1']}
|
bg={colors['white-1']}
|
||||||
p="lg"
|
p="lg"
|
||||||
radius="md"
|
radius="md"
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ function CreateDataPerpustakaan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Tombol Kembali */}
|
{/* Tombol Kembali */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -1,13 +1,31 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { 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 { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import HeaderSearch from '../../../_com/header';
|
import HeaderSearch from '../../../_com/header';
|
||||||
import perpustakaanDigitalState from '../../../_state/pendidikan/perpustakaan-digital';
|
import perpustakaanDigitalState from '../../../_state/pendidikan/perpustakaan-digital';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
|
|
||||||
function DataPerpustakaan() {
|
function DataPerpustakaan() {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
@@ -26,85 +44,138 @@ function DataPerpustakaan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListDataPerpustakaan({ search }: { search: string }) {
|
function ListDataPerpustakaan({ search }: { search: string }) {
|
||||||
const listDataState = useProxy(perpustakaanDigitalState.dataPerpustakaan)
|
const listDataState = useProxy(perpustakaanDigitalState.dataPerpustakaan);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const { data, page, totalPages, loading, load } = listDataState.findMany;
|
const { data, page, totalPages, loading, load } = listDataState.findMany;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load(page, 10, search);
|
load(page, 10, debouncedSearch);
|
||||||
}, [page, search]);
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const filteredData = data || [];
|
const filteredData = data || [];
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py="md">
|
||||||
<Skeleton height={500} radius="md" />
|
<Skeleton height={500} radius="md" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py="md">
|
||||||
<Paper withBorder bg={colors['white-1']} p="md" shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p="md" shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb="md">
|
||||||
<Title order={4}>List Data Perpustakaan</Title>
|
<Title order={4} size="md" lh={1.2}>
|
||||||
|
List Data Perpustakaan
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => router.push('/admin/pendidikan/perpustakaan-digital/data-perpustakaan/create')}
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
'/admin/pendidikan/perpustakaan-digital/data-perpustakaan/create'
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
|
||||||
<Table striped highlightOnHover withRowBorders style={{ minWidth: '700px' }}>
|
{/* Desktop Table */}
|
||||||
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
striped
|
||||||
|
highlightOnHover
|
||||||
|
withRowBorders
|
||||||
|
miw={0}
|
||||||
|
style={{ tableLayout: 'fixed', width: '100%' }}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '5%' }}>No</TableTh>
|
<TableTh style={{ width: '5%' }}>
|
||||||
<TableTh style={{ width: '25%' }}>Judul</TableTh>
|
<Text fz="xs" fw={600} lh={1.4} ta="center">
|
||||||
<TableTh style={{ width: '25%' }}>Kategori</TableTh>
|
No
|
||||||
<TableTh style={{ width: '23%' }}>Deskripsi</TableTh>
|
</Text>
|
||||||
<TableTh style={{ width: '22%' }}>Detail</TableTh>
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '25%' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
Judul
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '25%' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
Kategori
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '23%' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
Deskripsi
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '22%' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
Detail
|
||||||
|
</Text>
|
||||||
|
</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{filteredData.length > 0 ? (
|
{filteredData.length > 0 ? (
|
||||||
filteredData.map((item, index) => (
|
filteredData.map((item, index) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd style={{ width: '5%' }}>
|
<TableTd ta="center">
|
||||||
<Text truncate fz="sm">{index + 1}</Text>
|
<Text fz="sm" lh={1.5} truncate>
|
||||||
|
{index + 1}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ width: '20%' }}>
|
<TableTd>
|
||||||
<Text truncate fz="sm">{item.judul}</Text>
|
<Text fz="sm" lh={1.5} truncate>
|
||||||
|
{item.judul}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ width: '20%' }}>
|
<TableTd>
|
||||||
<Text truncate fz="sm">{item.kategori.name}</Text>
|
<Text fz="sm" lh={1.5} truncate>
|
||||||
|
{item.kategori.name}
|
||||||
|
</Text>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ width: '20%' }}>
|
<TableTd>
|
||||||
<Box w={150}>
|
<Text
|
||||||
<Text dangerouslySetInnerHTML={{ __html: item.deskripsi }} lineClamp={1} truncate fz="sm" />
|
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
||||||
</Box>
|
lineClamp={1}
|
||||||
|
fz="sm"
|
||||||
|
lh={1.5}
|
||||||
|
truncate
|
||||||
|
/>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ width: '20%' }}>
|
<TableTd>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="blue"
|
color="blue"
|
||||||
onClick={() => router.push(`/admin/pendidikan/perpustakaan-digital/data-perpustakaan/${item.id}`)}
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/pendidikan/perpustakaan-digital/data-perpustakaan/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<IconDeviceImacCog size={20} />
|
<IconDeviceImacCog size={20} />
|
||||||
<Text ml={5}>Detail</Text>
|
<Text ml={5} fz="sm" fw={500}>
|
||||||
|
Detail
|
||||||
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={4}>
|
<TableTd colSpan={5}>
|
||||||
<Center py={20}>
|
<Center py="xl">
|
||||||
<Text color="dimmed">Tidak ada data perpustakaan yang cocok</Text>
|
<Text c="dimmed" fz="sm" lh={1.5}>
|
||||||
|
Tidak ada data perpustakaan yang cocok
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -112,6 +183,80 @@ function ListDataPerpustakaan({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Cards */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="sm">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item, index) => (
|
||||||
|
<Paper key={item.id} withBorder p="sm" radius="md">
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
No
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4}>
|
||||||
|
{index + 1}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
Judul
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} truncate>
|
||||||
|
{item.judul}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
Kategori
|
||||||
|
</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.4} truncate>
|
||||||
|
{item.kategori.name}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4}>
|
||||||
|
Deskripsi
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
|
||||||
|
fz="sm"
|
||||||
|
fw={500}
|
||||||
|
lh={1.4}
|
||||||
|
lineClamp={2}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
fullWidth
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/pendidikan/perpustakaan-digital/data-perpustakaan/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconDeviceImacCog size={20} />
|
||||||
|
<Text ml={5} fz="sm" fw={500}>
|
||||||
|
Detail
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py="xl">
|
||||||
|
<Text c="dimmed" fz="sm" lh={1.5}>
|
||||||
|
Tidak ada data perpustakaan yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
@@ -128,7 +273,7 @@ function ListDataPerpustakaan({ search }: { search: string }) {
|
|||||||
</Center>
|
</Center>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DataPerpustakaan;
|
export default DataPerpustakaan;
|
||||||
@@ -75,7 +75,7 @@ function EditKategoriBuku() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ function CreateKategoriBuku() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 0, md: 'lg' }} py="xs">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
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 { useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -31,6 +31,7 @@ function ListKategoriBuku({ search }: { search: string }) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [modalHapus, setModalHapus] = useState(false)
|
const [modalHapus, setModalHapus] = useState(false)
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 1000);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -41,8 +42,8 @@ function ListKategoriBuku({ search }: { search: string }) {
|
|||||||
} = stateKategori.findMany;
|
} = stateKategori.findMany;
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 10, search)
|
load(page, 10, debouncedSearch)
|
||||||
}, [page, search]);
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
@@ -64,10 +65,12 @@ function ListKategoriBuku({ search }: { search: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={{ base: 'sm', md: 'lg' }}>
|
||||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
<Paper withBorder bg={colors['white-1']} p={{ base: 'md', md: 'lg' }} shadow="md" radius="md">
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb={{ base: 'sm', md: 'md' }}>
|
||||||
<Title order={4}>Daftar Kategori Buku</Title>
|
<Title order={4} lh={1.2}>
|
||||||
|
Daftar Kategori Buku
|
||||||
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={18} />}
|
leftSection={<IconPlus size={18} />}
|
||||||
color="blue"
|
color="blue"
|
||||||
@@ -77,40 +80,62 @@ function ListKategoriBuku({ search }: { search: string }) {
|
|||||||
Tambah Baru
|
Tambah Baru
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
<Box style={{ overflowX: 'auto' }}>
|
|
||||||
<Table highlightOnHover>
|
{/* Desktop Table */}
|
||||||
|
<Box visibleFrom="md">
|
||||||
|
<Table
|
||||||
|
highlightOnHover
|
||||||
|
miw={0}
|
||||||
|
style={{
|
||||||
|
tableLayout: 'fixed',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh style={{ width: '10%' }}>No</TableTh>
|
<TableTh style={{ width: '10%' }}>
|
||||||
<TableTh style={{ width: '60%' }}>Nama Kategori</TableTh>
|
<Text fz="xs" fw={600} lh={1.4} c="dimmed" ta="center">No</Text>
|
||||||
<TableTh style={{ width: '15%' }}>Edit</TableTh>
|
</TableTh>
|
||||||
<TableTh style={{ width: '15%' }}>Hapus</TableTh>
|
<TableTh style={{ width: '60%' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4} c="dimmed">Nama Kategori</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '15%' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4} c="dimmed" ta="center">Edit</Text>
|
||||||
|
</TableTh>
|
||||||
|
<TableTh style={{ width: '15%' }}>
|
||||||
|
<Text fz="xs" fw={600} lh={1.4} c="dimmed" ta="center">Hapus</Text>
|
||||||
|
</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{filteredData.length > 0 ? (
|
{filteredData.length > 0 ? (
|
||||||
filteredData.map((item, index) => (
|
filteredData.map((item, index) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>{index + 1}</TableTd>
|
<TableTd ta="center">
|
||||||
<TableTd>
|
<Text fz="sm" fw={500} lh={1.5}>{index + 1}</Text>
|
||||||
<Text truncate fz="sm">{item.name}</Text>
|
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button variant="light" color="green" onClick={() => router.push(`/admin/pendidikan/perpustakaan-digital/kategori-buku/${item.id}`)}>
|
<Text truncate fz="sm" fw={500} lh={1.5}>
|
||||||
<IconEdit size={20} />
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd ta="center">
|
||||||
|
<Button variant="light" color="green" size="xs" onClick={() => router.push(`/admin/pendidikan/perpustakaan-digital/kategori-buku/${item.id}`)}>
|
||||||
|
<IconEdit size={16} />
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd ta="center">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="red"
|
color="red"
|
||||||
|
size="xs"
|
||||||
disabled={perpustakaanDigitalState.kategoriBuku.delete.loading}
|
disabled={perpustakaanDigitalState.kategoriBuku.delete.loading}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedId(item.id)
|
setSelectedId(item.id)
|
||||||
setModalHapus(true)
|
setModalHapus(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconTrash size={20} />
|
<IconTrash size={16} />
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -119,7 +144,9 @@ function ListKategoriBuku({ search }: { search: string }) {
|
|||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={4}>
|
<TableTd colSpan={4}>
|
||||||
<Center py={20}>
|
<Center py={20}>
|
||||||
<Text c="dimmed">Tidak ada data kategori buku yang cocok</Text>
|
<Text c="dimmed" fz="sm" fw={500} lh={1.5}>
|
||||||
|
Tidak ada data kategori buku yang cocok
|
||||||
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
@@ -127,7 +154,59 @@ function ListKategoriBuku({ search }: { search: string }) {
|
|||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Card View */}
|
||||||
|
<Box hiddenFrom="md">
|
||||||
|
<Stack gap="md">
|
||||||
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item, index) => (
|
||||||
|
<Paper key={item.id} withBorder radius="md" p="md" bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>No</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.5}>{index + 1}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw={600} lh={1.4}>Nama Kategori</Text>
|
||||||
|
<Text fz="sm" fw={500} lh={1.5}>{item.name}</Text>
|
||||||
|
</Box>
|
||||||
|
<Group justify="flex-end" mt="xs">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="green"
|
||||||
|
size="xs"
|
||||||
|
onClick={() => router.push(`/admin/pendidikan/perpustakaan-digital/kategori-buku/${item.id}`)}
|
||||||
|
>
|
||||||
|
<IconEdit size={16} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="red"
|
||||||
|
size="xs"
|
||||||
|
disabled={perpustakaanDigitalState.kategoriBuku.delete.loading}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(item.id)
|
||||||
|
setModalHapus(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconTrash size={16} />
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Center py={20}>
|
||||||
|
<Text c="dimmed" fz="sm" fw={500} lh={1.5}>
|
||||||
|
Tidak ada data kategori buku yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
|
||||||
<Center>
|
<Center>
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
@@ -136,13 +215,14 @@ function ListKategoriBuku({ search }: { search: string }) {
|
|||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
}}
|
}}
|
||||||
total={totalPages}
|
total={totalPages}
|
||||||
mt="md"
|
mt={{ base: 'sm', md: 'md' }}
|
||||||
mb="md"
|
mb={{ base: 'sm', md: 'md' }}
|
||||||
color="blue"
|
color="blue"
|
||||||
radius="md"
|
radius="md"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
|
||||||
|
|
||||||
{/* Modal Konfirmasi Hapus */}
|
{/* Modal Konfirmasi Hapus */}
|
||||||
<ModalKonfirmasiHapus
|
<ModalKonfirmasiHapus
|
||||||
opened={modalHapus}
|
opened={modalHapus}
|
||||||
|
|||||||
@@ -1,8 +1,29 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import LayoutTabs from './_lib/layoutTabs';
|
import LayoutTabs from './_lib/layoutTabs';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
import { Box } from '@mantine/core';
|
||||||
|
|
||||||
function Layout({ children }: { children: React.ReactNode }) {
|
function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Contoh path:
|
||||||
|
// - /darmasaba/desa/berita/semua → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan → panjang 5 → list
|
||||||
|
// - /darmasaba/desa/berita/Pemerintahan/123 → panjang 6 → detail
|
||||||
|
|
||||||
|
const segments = pathname.split('/').filter(Boolean);
|
||||||
|
const isDetailPage = segments.length >= 5;
|
||||||
|
|
||||||
|
if (isDetailPage) {
|
||||||
|
// Tampilkan tanpa tab menu
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutTabs>
|
<LayoutTabs>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user