Fix UI Menu Landing Page, Submenu Profile & Desa Anti Korupsi

This commit is contained in:
2025-08-01 15:21:01 +08:00
parent 024d5517fa
commit 54312e9486
24 changed files with 875 additions and 252 deletions

View File

@@ -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;
}
},
},

View File

@@ -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;
}
},
},

View File

@@ -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}

View File

@@ -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>
)
}

View File

@@ -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>
);
}

View File

@@ -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>

View File

@@ -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>
);
}

View File

@@ -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 }

View File

@@ -11,7 +11,7 @@ const DesaAntiKorupsi = new Elysia({
})
// ✅ Find all
.get("/find-many", desaAntiKorupsiFindMany)
.get("/findMany", desaAntiKorupsiFindMany)
// ✅ Find by ID
.get("/:id", desaAntiKorupsiFindUnique)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 },

View File

@@ -11,7 +11,7 @@ const MediaSosial = new Elysia({
})
// ✅ Find all
.get("/find-many", MediaSosialFindMany)
.get("/findMany", MediaSosialFindMany)
// ✅ Find by ID
.get("/:id", MediaSosialFindUnique)

View File

@@ -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;

View File

@@ -11,7 +11,7 @@ const ProgramInovasi = new Elysia({
})
// ✅ Find all
.get("/find-many", ProgramInovasiFindMany)
.get("/findMany", ProgramInovasiFindMany)
// ✅ Find by ID
.get("/:id", ProgramInovasiFindUnique)

View File

@@ -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>
);
}