Fix UI Menu Landing Page, Submenu Profile & Desa Anti Korupsi
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -54,20 +55,38 @@ const desaAntikorupsi = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<
|
||||
Prisma.DesaAntiKorupsiGetPayload<{
|
||||
include: {
|
||||
file: true;
|
||||
kategori: true;
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.desaantikorupsi[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
desaAntikorupsi.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
desaAntikorupsi.findMany.loading = true; // Use the full path to access the property
|
||||
desaAntikorupsi.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.desaantikorupsi[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
desaAntikorupsi.findMany.data = res.data.data || [];
|
||||
desaAntikorupsi.findMany.total = res.data.total || 0;
|
||||
desaAntikorupsi.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load media sosial:", res.data?.message);
|
||||
desaAntikorupsi.findMany.data = [];
|
||||
desaAntikorupsi.findMany.total = 0;
|
||||
desaAntikorupsi.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading media sosial:", error);
|
||||
desaAntikorupsi.findMany.data = [];
|
||||
desaAntikorupsi.findMany.total = 0;
|
||||
desaAntikorupsi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
desaAntikorupsi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -281,14 +300,38 @@ const kategoriDesaAntiKorupsi = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
}> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.kategoridak["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
kategoriDesaAntiKorupsi.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
kategoriDesaAntiKorupsi.findMany.loading = true; // Use the full path to access the property
|
||||
kategoriDesaAntiKorupsi.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.kategoridak[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
kategoriDesaAntiKorupsi.findMany.data = res.data.data || [];
|
||||
kategoriDesaAntiKorupsi.findMany.total = res.data.total || 0;
|
||||
kategoriDesaAntiKorupsi.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load media sosial:", res.data?.message);
|
||||
kategoriDesaAntiKorupsi.findMany.data = [];
|
||||
kategoriDesaAntiKorupsi.findMany.total = 0;
|
||||
kategoriDesaAntiKorupsi.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading media sosial:", error);
|
||||
kategoriDesaAntiKorupsi.findMany.data = [];
|
||||
kategoriDesaAntiKorupsi.findMany.total = 0;
|
||||
kategoriDesaAntiKorupsi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
kategoriDesaAntiKorupsi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -5,10 +6,10 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateProgramInovasi = z.object({
|
||||
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||
description: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
name: z.string().min(1, "Nama minimal 1 karakter"),
|
||||
description: z.string().min(1, "Deskripsi minimal 1 karakter"),
|
||||
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||
link: z.string().min(3, "Link minimal 3 karakter"),
|
||||
link: z.string().min(1, "Link minimal 1 karakter"),
|
||||
});
|
||||
|
||||
type ProgramInovasiForm = Prisma.ProgramInovasiGetPayload<{
|
||||
@@ -59,15 +60,38 @@ const programInovasi = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.ProgramInovasiGetPayload<{ include: { image: true } }>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.programinovasi[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
programInovasi.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
programInovasi.findMany.loading = true; // Use the full path to access the property
|
||||
programInovasi.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.programinovasi[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
programInovasi.findMany.data = res.data.data || [];
|
||||
programInovasi.findMany.total = res.data.total || 0;
|
||||
programInovasi.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load pegawai:", res.data?.message);
|
||||
programInovasi.findMany.data = [];
|
||||
programInovasi.findMany.total = 0;
|
||||
programInovasi.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading pegawai:", error);
|
||||
programInovasi.findMany.data = [];
|
||||
programInovasi.findMany.total = 0;
|
||||
programInovasi.findMany.totalPages = 1;
|
||||
} finally {
|
||||
programInovasi.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -453,13 +477,38 @@ const mediaSosial = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.MediaSosialGetPayload<{ include: { image: true } }>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.mediasosial["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
mediaSosial.findMany.data = res.data?.data ?? [];
|
||||
data: null as any[] | null,
|
||||
page: 1,
|
||||
totalPages: 1,
|
||||
total: 0,
|
||||
loading: false,
|
||||
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||
mediaSosial.findMany.loading = true; // Use the full path to access the property
|
||||
mediaSosial.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.mediasosial[
|
||||
"findMany"
|
||||
].get({
|
||||
query: { page, limit },
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
mediaSosial.findMany.data = res.data.data || [];
|
||||
mediaSosial.findMany.total = res.data.total || 0;
|
||||
mediaSosial.findMany.totalPages = res.data.totalPages || 1;
|
||||
} else {
|
||||
console.error("Failed to load media sosial:", res.data?.message);
|
||||
mediaSosial.findMany.data = [];
|
||||
mediaSosial.findMany.total = 0;
|
||||
mediaSosial.findMany.totalPages = 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading media sosial:", error);
|
||||
mediaSosial.findMany.data = [];
|
||||
mediaSosial.findMany.total = 0;
|
||||
mediaSosial.findMany.totalPages = 1;
|
||||
} finally {
|
||||
mediaSosial.findMany.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
@@ -34,6 +34,14 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const router = useRouter()
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = stateKategori.findMany;
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
stateKategori.delete.byId(selectedId)
|
||||
@@ -42,23 +50,51 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
||||
}
|
||||
}
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateKategori.findMany.load()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
load(page, 10)
|
||||
}, [page])
|
||||
|
||||
const filteredData = (stateKategori.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
}, [data, search]);
|
||||
|
||||
if (!stateKategori.findMany.data) {
|
||||
// Handle loading state
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
<Skeleton height={300} />
|
||||
</Stack>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Kategori Kegiatan'
|
||||
href='/admin/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/create'
|
||||
/>
|
||||
<Box style={{ overflowX: "auto" }}>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama</TableTh>
|
||||
<TableTh>Edit</TableTh>
|
||||
<TableTh>Delete</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -68,38 +104,50 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
||||
title='List Kategori Kegiatan'
|
||||
href='/admin/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/create'
|
||||
/>
|
||||
<Box style={{overflowY: "auto"}}>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Kategori</TableTh>
|
||||
<TableTh>Edit</TableTh>
|
||||
<TableTh>Delete</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
<Button color="green" onClick={() => router.push(`/admin/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/${item.id}`)}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button color="red" onClick={() => {
|
||||
setSelectedId(item.id)
|
||||
setModalHapus(true)
|
||||
}}>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<Box style={{ overflowY: "auto" }}>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Kategori</TableTh>
|
||||
<TableTh>Edit</TableTh>
|
||||
<TableTh>Delete</TableTh>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
<Button color="green" onClick={() => router.push(`/admin/landing-page/desa-anti-korupsi/kategori-desa-anti-korupsi/${item.id}`)}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button color="red" onClick={() => {
|
||||
setSelectedId(item.id)
|
||||
setModalHapus(true)
|
||||
}}>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => {
|
||||
load(newPage, 10);
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
/>
|
||||
</Center>
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
@@ -29,27 +29,65 @@ function DesaAntiKorupsi() {
|
||||
function ListDesaAntiKorupsi({ search }: { search: string }) {
|
||||
const listState = useProxy(korupsiState.desaAntikorupsi)
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = listState.findMany;
|
||||
|
||||
useEffect(() => {
|
||||
listState.findMany.load()
|
||||
}, [])
|
||||
load(page, 10);
|
||||
}, [page]);
|
||||
|
||||
const filteredData = (listState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword) ||
|
||||
item.kategori?.name?.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi?.toLowerCase().includes(keyword) ||
|
||||
item.kategori?.name?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
.sort((a, b) => b.createdAt - a.createdAt);
|
||||
}, [data, search]);
|
||||
|
||||
if (!listState.findMany.data) {
|
||||
// Handle loading state
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
<Skeleton height={300} />
|
||||
</Stack>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Desa Anti Korupsi'
|
||||
href='/admin/landing-page/desa-anti-korupsi/list-desa-anti-korupsi/create'
|
||||
/>
|
||||
<Box style={{ overflowX: "auto" }}>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama</TableTh>
|
||||
<TableTh>Deskripsi</TableTh>
|
||||
<TableTh>Kategori</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
@@ -77,7 +115,9 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||
<Box w={200}>
|
||||
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>{item.kategori?.name}</TableTd>
|
||||
<TableTd>
|
||||
@@ -92,6 +132,18 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => {
|
||||
load(newPage, 10);
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
@@ -30,24 +30,61 @@ function ListMediaSosial({ search }: { search: string }) {
|
||||
const stateMediaSosial = useProxy(profileLandingPageState.mediaSosial)
|
||||
const router = useRouter();
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateMediaSosial.findMany.load()
|
||||
}, [])
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = stateMediaSosial.findMany;
|
||||
|
||||
const filteredData = (stateMediaSosial.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.iconUrl?.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
useEffect(() => {
|
||||
load(page, 10)
|
||||
}, [page])
|
||||
|
||||
if (!stateMediaSosial.findMany.data) {
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword) ||
|
||||
item.iconUrl?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
}, [data, search]);
|
||||
|
||||
// Handle loading state
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
<Skeleton height={300} />
|
||||
</Stack>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Media Sosial'
|
||||
href='/admin/landing-page/profile/media-sosial/create'
|
||||
/>
|
||||
<Box style={{ overflowX: "auto" }}>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Media Sosial / Nama Kontak</TableTh>
|
||||
<TableTh>Image</TableTh>
|
||||
<TableTh>Icon URL / No Telephone</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -57,37 +94,55 @@ function ListMediaSosial({ search }: { search: string }) {
|
||||
title='List Media Sosial'
|
||||
href='/admin/landing-page/profile/media-sosial/create'
|
||||
/>
|
||||
<Box style={{overflowY: "auto"}}>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Media Sosial / Nama Kontak</TableTh>
|
||||
<TableTh>Image</TableTh>
|
||||
<TableTh>Icon URL / No Telephone</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
<Box w={50} h={50}>
|
||||
<Image src={item.image?.link} alt={item.name} />
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>{item.iconUrl}</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/landing-page/profile/media-sosial/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<Box style={{ overflowY: "auto" }}>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Media Sosial / Nama Kontak</TableTh>
|
||||
<TableTh>Image</TableTh>
|
||||
<TableTh>Icon URL / No Telephone</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
<Box w={50} h={50}>
|
||||
<Image src={item.image?.link} alt={item.name} />
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Box w={250}>
|
||||
<a style={{color: "black"}} href={item.iconUrl} target="_blank" rel="noopener noreferrer">
|
||||
<Text truncate fz={'sm'}>{item.iconUrl}</Text>
|
||||
</a>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/landing-page/profile/media-sosial/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => {
|
||||
load(newPage, 10);
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,13 @@ function DetailProgramInovasi() {
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Link</Text>
|
||||
<Text fz={"lg"}>{stateProgramInovasi.findUnique.data?.link}</Text>
|
||||
<a
|
||||
href={stateProgramInovasi.findUnique.data?.link || "#"}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{stateProgramInovasi.findUnique.data?.link || "Tidak ada link"}
|
||||
</a>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
@@ -30,27 +30,62 @@ function ListProgramInovasi({ search }: { search: string }) {
|
||||
const stateProgramInovasi = useProxy(profileLandingPageState.programInovasi)
|
||||
const router = useRouter();
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateProgramInovasi.findMany.load()
|
||||
}, [])
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = stateProgramInovasi.findMany;
|
||||
|
||||
const filteredData = (stateProgramInovasi.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.description?.toLowerCase().includes(keyword) ||
|
||||
item.link?.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
useEffect(() => {
|
||||
load(page, 10);
|
||||
}, [page]);
|
||||
|
||||
if (!stateProgramInovasi.findMany.data) {
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword) ||
|
||||
item.description?.toLowerCase().includes(keyword) ||
|
||||
item.link?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
}, [data, search]);
|
||||
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
<Skeleton height={300} />
|
||||
</Stack>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Program Inovasi'
|
||||
href='/admin/landing-page/profile/program-inovasi/create'
|
||||
/>
|
||||
<Box style={{ overflowX: "auto" }}>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Program</TableTh>
|
||||
<TableTh>Deskripsi</TableTh>
|
||||
<TableTh>Link</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
@@ -72,8 +107,14 @@ function ListProgramInovasi({ search }: { search: string }) {
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>{item.description}</TableTd>
|
||||
<TableTd>{item.link}</TableTd>
|
||||
<TableTd w={200}>{item.description}</TableTd>
|
||||
<TableTd>
|
||||
<Box w={250}>
|
||||
<a style={{ color: "black" }} href={item.link} target="_blank" rel="noopener noreferrer">
|
||||
<Text truncate fz={'sm'}>{item.link}</Text>
|
||||
</a>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/landing-page/profile/program-inovasi/${item.id}`)}>
|
||||
<IconDeviceImac size={20} />
|
||||
@@ -85,6 +126,18 @@ function ListProgramInovasi({ search }: { search: string }) {
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
<Center>
|
||||
<Pagination
|
||||
value={page}
|
||||
onChange={(newPage) => {
|
||||
load(newPage, 10);
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
total={totalPages}
|
||||
mt="md"
|
||||
mb="md"
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ async function desaAntiKorupsiFindMany(context: Context) {
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: 'desc' }, // opsional, kalau mau urut berdasarkan waktu
|
||||
orderBy: { name: 'asc' }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.desaAntiKorupsi.count({
|
||||
where: { isActive: true }
|
||||
|
||||
@@ -11,7 +11,7 @@ const DesaAntiKorupsi = new Elysia({
|
||||
})
|
||||
|
||||
// ✅ Find all
|
||||
.get("/find-many", desaAntiKorupsiFindMany)
|
||||
.get("/findMany", desaAntiKorupsiFindMany)
|
||||
|
||||
// ✅ Find by ID
|
||||
.get("/:id", desaAntiKorupsiFindUnique)
|
||||
|
||||
@@ -1,15 +1,40 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function kategoriDesaAntiKorupsiFindMany() {
|
||||
const data = await prisma.kategoriDesaAntiKorupsi.findMany();
|
||||
return {
|
||||
success: true,
|
||||
data: data.map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
}
|
||||
}),
|
||||
async function kategoriDesaAntiKorupsiFindMany(context: Context) {
|
||||
const page = Number(context.query.page) || 1;
|
||||
const limit = Number(context.query.limit) || 10;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
try {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.kategoriDesaAntiKorupsi.findMany({
|
||||
where: { isActive: true },
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { name: 'asc' }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.kategoriDesaAntiKorupsi.count({
|
||||
where: { isActive: true }
|
||||
})
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success fetch kategori desa anti korupsi with pagination",
|
||||
data,
|
||||
page,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
total,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Find many paginated error:", e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Failed fetch kategori desa anti korupsi with pagination",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default kategoriDesaAntiKorupsiFindMany;
|
||||
|
||||
@@ -9,7 +9,7 @@ const KategoriDesaAntiKorupsi = new Elysia({
|
||||
prefix: "/kategoridak",
|
||||
tags: ["Landing Page/Desa Anti Korupsi"],
|
||||
})
|
||||
.get("/find-many", kategoriDesaAntiKorupsiFindMany)
|
||||
.get("/findMany", kategoriDesaAntiKorupsiFindMany)
|
||||
.get("/:id", async (context) => {
|
||||
const response = await kategoriDesaAntiKorupsiFindUnique(context);
|
||||
return response;
|
||||
|
||||
@@ -16,7 +16,7 @@ async function mediaSosialFindMany(context: Context) {
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.mediaSosial.count({
|
||||
where: { isActive: true },
|
||||
|
||||
@@ -11,7 +11,7 @@ const MediaSosial = new Elysia({
|
||||
})
|
||||
|
||||
// ✅ Find all
|
||||
.get("/find-many", MediaSosialFindMany)
|
||||
.get("/findMany", MediaSosialFindMany)
|
||||
|
||||
// ✅ Find by ID
|
||||
.get("/:id", MediaSosialFindUnique)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// /api/berita/findManyPaginated.ts
|
||||
// // /api/berita/findManyPaginated.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
@@ -16,7 +16,7 @@ async function programInovasiFindMany(context: Context) {
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.programInovasi.count({
|
||||
where: { isActive: true },
|
||||
@@ -41,3 +41,4 @@ async function programInovasiFindMany(context: Context) {
|
||||
}
|
||||
|
||||
export default programInovasiFindMany;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const ProgramInovasi = new Elysia({
|
||||
})
|
||||
|
||||
// ✅ Find all
|
||||
.get("/find-many", ProgramInovasiFindMany)
|
||||
.get("/findMany", ProgramInovasiFindMany)
|
||||
|
||||
// ✅ Find by ID
|
||||
.get("/:id", ProgramInovasiFindUnique)
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import images from "@/con/images";
|
||||
import profileLandingPageState from "@/app/admin/(dashboard)/_state/landing-page/profile";
|
||||
import { Center, Image, Paper, SimpleGrid } from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import { motion } from 'framer-motion';
|
||||
import { useTransitionRouter } from 'next-view-transitions';
|
||||
import { useProxy } from "valtio/utils";
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
||||
|
||||
const listImageModule = Object.values(images.module);
|
||||
type ProgramInovasiItem = Prisma.ProgramInovasiGetPayload<{ include: { image: true } }>;
|
||||
|
||||
function ModuleItem({ item }: { item: string }) {
|
||||
function ModuleItem({ data }: { data: ProgramInovasiItem }) {
|
||||
const router = useTransitionRouter();
|
||||
return (
|
||||
<Paper
|
||||
onClick={() => {
|
||||
router.push(`/module/c`);
|
||||
router.push(`/${data.name}`);
|
||||
}}
|
||||
p={"md"}
|
||||
bg={"white"}
|
||||
@@ -22,7 +25,7 @@ function ModuleItem({ item }: { item: string }) {
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.05 }}
|
||||
>
|
||||
<Image src={item} alt="icon"
|
||||
<Image src={data.image?.link || ""} alt="icon"
|
||||
fit="contain"
|
||||
sizes="100%"
|
||||
loading="lazy"
|
||||
@@ -38,6 +41,11 @@ function ModuleItem({ item }: { item: string }) {
|
||||
}
|
||||
|
||||
function ModuleView() {
|
||||
const listImageState = useProxy(profileLandingPageState.programInovasi)
|
||||
|
||||
useShallowEffect(() => {
|
||||
listImageState.findMany.load()
|
||||
}, [])
|
||||
return (
|
||||
<SimpleGrid
|
||||
cols={{
|
||||
@@ -45,9 +53,9 @@ function ModuleView() {
|
||||
md: 3,
|
||||
}}
|
||||
>
|
||||
{listImageModule.map((item, k) => {
|
||||
return <ModuleItem key={k} item={item} />;
|
||||
})}
|
||||
{listImageState.findMany.data?.map((item) => (
|
||||
<ModuleItem key={item.id} data={item} />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user