Sinkronisasi UI Admin & User Menu Landing Page, Submenu Profile, SDGSDesa
This commit is contained in:
114
prisma/data/landing-page/sdgs-desa/sdgs-desa.json
Normal file
114
prisma/data/landing-page/sdgs-desa/sdgs-desa.json
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "cmdsjzdl30002vneknuvo4irv",
|
||||||
|
"name": "Desa Tanpa Kemiskinan",
|
||||||
|
"jumlah": "52.62",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskargd0005vnek0mu2ofk9",
|
||||||
|
"name": "Desa Tanpa Kelaparan",
|
||||||
|
"jumlah": "35.75",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskbvl0008vnek5dmieatb",
|
||||||
|
"name": "Desa Sehat Dan Sejahtera",
|
||||||
|
"jumlah": "77.37",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskcx91000bvneko7tuaoqa",
|
||||||
|
"name": "Pendidikan Desa Berkualitas",
|
||||||
|
"jumlah": "34.11",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskjare000evnek1hglu0x8",
|
||||||
|
"name": "Keterlibatan Perempuan Desa",
|
||||||
|
"jumlah": "45.70",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskqcpc0002vnvnqjkqgm92",
|
||||||
|
"name": "Desa Layak Air Bersih Dan Sanitasi",
|
||||||
|
"jumlah": "48.54",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdsktl3x0005vnvne15seefw",
|
||||||
|
"name": "Desa Berenergi Bersih Dan Terbarukan",
|
||||||
|
"jumlah": "99.64",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskuncw0008vnvcsdqoeog",
|
||||||
|
"name": "Pertumbuhan Ekonomi Desa Merata",
|
||||||
|
"jumlah": "40.92",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskw83j000bvvn9szqrea6",
|
||||||
|
"name": "Infrastruktur Dan Inovasi Desa Sesuai Kebutuhan",
|
||||||
|
"jumlah": "35.37",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskwrq7000envnvy0c5nbgf",
|
||||||
|
"name": "Desa Tanpa Kesenjangan",
|
||||||
|
"jumlah": "35.47",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskxivx000hnvnvsx520gv1",
|
||||||
|
"name": "Kawasan Pemukiman Desa Aman Dan Nyaman",
|
||||||
|
"jumlah": "40.35",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdskzg4c000kvnnkiv61gkt",
|
||||||
|
"name": "Konsumsi Dan Produksi Desa Sadar Lingkungan",
|
||||||
|
"jumlah": "16.67",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdsl07lk000nvnnvnrepsdy5m",
|
||||||
|
"name": "Desa Tanggap Perubahan Iklim",
|
||||||
|
"jumlah": "0.00",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdsl10rq000qvnvnlch9c1yv",
|
||||||
|
"name": "Desa Peduli Lingkungan Laut",
|
||||||
|
"jumlah": "50.00",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdsl1mc2000tvnvn357n8usi",
|
||||||
|
"name": "Desa Peduli Lingkungan Darat",
|
||||||
|
"jumlah": "0.00",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdsl2bx3000wvnvntshi4gnj",
|
||||||
|
"name": "Desa Damai Berkeadilan",
|
||||||
|
"jumlah": "78.65",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdsl2yz3000zvnvnmf60ok7q",
|
||||||
|
"name": "Kemitraan Untuk Pembangunan Desa",
|
||||||
|
"jumlah": "20.00",
|
||||||
|
"imageId": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cmdsl492h0012vnvnmckm3n2x",
|
||||||
|
"name": "Kelembagaan Desa Dinamis Dan Budaya Desa Adaptif",
|
||||||
|
"jumlah": "47.22",
|
||||||
|
"imageId": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -171,8 +171,8 @@ model SDGSDesa {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String @unique
|
name String @unique
|
||||||
jumlah String
|
jumlah String
|
||||||
image FileStorage @relation(fields: [imageId], references: [id])
|
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||||
imageId String
|
imageId String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -52,19 +53,38 @@ const sdgsDesa = proxy({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as Array<
|
data: null as any[] | null,
|
||||||
Prisma.SDGSDesaGetPayload<{
|
page: 1,
|
||||||
include: {
|
totalPages: 1,
|
||||||
image: true;
|
total: 0,
|
||||||
};
|
loading: false,
|
||||||
}>
|
load: async (page = 1, limit = 10) => { // Change to arrow function
|
||||||
> | null,
|
sdgsDesa.findMany.loading = true; // Use the full path to access the property
|
||||||
async load() {
|
sdgsDesa.findMany.page = page;
|
||||||
const res = await ApiFetch.api.landingpage.sdgsdesa[
|
try {
|
||||||
"find-many"
|
const res = await ApiFetch.api.landingpage.sdgsdesa[
|
||||||
].get();
|
"findMany"
|
||||||
if (res.status === 200) {
|
].get({
|
||||||
sdgsDesa.findMany.data = res.data?.data ?? [];
|
query: { page, limit },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
sdgsDesa.findMany.data = res.data.data || [];
|
||||||
|
sdgsDesa.findMany.total = res.data.total || 0;
|
||||||
|
sdgsDesa.findMany.totalPages = res.data.totalPages || 1;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load media sosial:", res.data?.message);
|
||||||
|
sdgsDesa.findMany.data = [];
|
||||||
|
sdgsDesa.findMany.total = 0;
|
||||||
|
sdgsDesa.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading media sosial:", error);
|
||||||
|
sdgsDesa.findMany.data = [];
|
||||||
|
sdgsDesa.findMany.total = 0;
|
||||||
|
sdgsDesa.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
sdgsDesa.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ function ListKategoriKegiatan({ search }: { search: string }) {
|
|||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
<Skeleton height={300} />
|
<Skeleton height={550} />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
|
|||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
<Skeleton height={300} />
|
<Skeleton height={550} />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ function ListMediaSosial({ search }: { search: string }) {
|
|||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
<Skeleton height={300} />
|
<Skeleton height={550} />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ function ListProgramInovasi({ search }: { search: string }) {
|
|||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
<Skeleton height={300} />
|
<Skeleton height={550} />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ function EditKolaborasiInovasi() {
|
|||||||
<iframe
|
<iframe
|
||||||
src={previewImage}
|
src={previewImage}
|
||||||
width="100%"
|
width="100%"
|
||||||
height="500px"
|
height="250px"
|
||||||
style={{ border: "1px solid #ccc", borderRadius: "8px" }}
|
style={{ border: "1px solid #ccc", borderRadius: "8px" }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ function CreateSDGsDesa() {
|
|||||||
<iframe
|
<iframe
|
||||||
src={previewImage}
|
src={previewImage}
|
||||||
width="100%"
|
width="100%"
|
||||||
height="500px"
|
height="250px"
|
||||||
style={{ border: "1px solid #ccc", borderRadius: "8px" }}
|
style={{ border: "1px solid #ccc", borderRadius: "8px" }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* 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, 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 { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import HeaderSearch from '../../_com/header';
|
import HeaderSearch from '../../_com/header';
|
||||||
import sdgsDesa from '../../_state/landing-page/sdgs-desa';
|
import sdgsDesa from '../../_state/landing-page/sdgs-desa';
|
||||||
@@ -30,24 +30,61 @@ function SdgsDesa() {
|
|||||||
function ListSdgsDesa({ search }: { search: string }) {
|
function ListSdgsDesa({ search }: { search: string }) {
|
||||||
const listState = useProxy(sdgsDesa)
|
const listState = useProxy(sdgsDesa)
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
page,
|
||||||
|
totalPages,
|
||||||
|
loading,
|
||||||
|
load,
|
||||||
|
} = listState.findMany;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
listState.findMany.load()
|
load(page, 10)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const filteredData = (listState.findMany.data || []).filter(item => {
|
const filteredData = useMemo(() => {
|
||||||
const keyword = search.toLowerCase();
|
if (!data) return [];
|
||||||
return (
|
return data.filter(item => {
|
||||||
item.name.toLowerCase().includes(keyword) ||
|
const keyword = search.toLowerCase();
|
||||||
item.jumlah.toLowerCase().includes(keyword)
|
return (
|
||||||
)
|
item.name?.toLowerCase().includes(keyword) ||
|
||||||
});
|
item.jumlah?.toLowerCase().includes(keyword)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}, [data, search]);
|
||||||
|
|
||||||
if (!listState.findMany.data) {
|
// Handle loading state
|
||||||
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
<Skeleton h={500} />
|
<Skeleton height={550} />
|
||||||
</Stack>
|
</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 SDGs Desa</TableTh>
|
||||||
|
<TableTh>Jumlah SDGs Desa</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -90,6 +127,18 @@ function ListSdgsDesa({ search }: { search: string }) {
|
|||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
<Center>
|
||||||
|
<Pagination
|
||||||
|
value={page}
|
||||||
|
onChange={(newPage) => {
|
||||||
|
load(newPage, 10);
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}}
|
||||||
|
total={totalPages}
|
||||||
|
mt="md"
|
||||||
|
mb="md"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ async function sdgsDesaFindMany(context: Context) {
|
|||||||
},
|
},
|
||||||
skip,
|
skip,
|
||||||
take: limit,
|
take: limit,
|
||||||
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
orderBy: { jumlah: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||||
}),
|
}),
|
||||||
prisma.sDGSDesa.count({
|
prisma.sDGSDesa.count({
|
||||||
where: { isActive: true },
|
where: { isActive: true },
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const SDGSDesa = new Elysia({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// ✅ Find all
|
// ✅ Find all
|
||||||
.get("/find-many", sdgsDesaFindMany)
|
.get("/findMany", sdgsDesaFindMany)
|
||||||
|
|
||||||
// ✅ Find by ID
|
// ✅ Find by ID
|
||||||
.get("/:id", sdgsDesaFindUnique)
|
.get("/:id", sdgsDesaFindUnique)
|
||||||
|
|||||||
@@ -1,11 +1,43 @@
|
|||||||
import { Box, Center, Container, Image, Stack, Text } from '@mantine/core';
|
'use client'
|
||||||
|
import { Box, Center, Container, Image, LoadingOverlay, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import BackButton from '../../(pages)/desa/layanan/_com/BackButto';
|
import BackButton from '../../(pages)/desa/layanan/_com/BackButto';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
const [sdgsDesa, setSdgsDesa] = useState<Prisma.SDGSDesaGetPayload<{ include: { image: true } }>[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSdgsDesa = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/landingpage/sdgsdesa/findMany');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
// Ensure the data is an array before setting it
|
||||||
|
let data = [];
|
||||||
|
if (Array.isArray(result.data)) {
|
||||||
|
data = result.data;
|
||||||
|
} else if (Array.isArray(result)) {
|
||||||
|
// In case the API returns the array directly
|
||||||
|
data = result;
|
||||||
|
} else {
|
||||||
|
console.error('Invalid data format:', result);
|
||||||
|
}
|
||||||
|
setSdgsDesa(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching sdgs desa:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchSdgsDesa();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
<Stack pos={"relative"} py={"xl"} gap={22}>
|
<Stack pos={"relative"} py={"xl"} gap={22}>
|
||||||
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
|
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
|
||||||
<Container w={{ base: "100%", md: "50%" }}>
|
<Container w={{ base: "100%", md: "50%" }}>
|
||||||
@@ -28,12 +60,70 @@ function Page() {
|
|||||||
>
|
>
|
||||||
SDGs Desa sebagaimana dijabarkan dalam Permendesa Nomor 21 tahun 2020 terdiri dari 18 tujuan yang harus dicapai pada tahun 2030. Tujuan-tujuan tersebut mencakup berbagai aspek kehidupan masyarakat desa, mulai dari pengentasan kemiskinan, peningkatan kesehatan dan pendidikan, kesetaraan gender, pertumbuhan ekonomi, infrastruktur, hingga kelestarian lingkungan. Adapun SDGs Desa terdiri dari tujuan-tujuan sebagai berikut:
|
SDGs Desa sebagaimana dijabarkan dalam Permendesa Nomor 21 tahun 2020 terdiri dari 18 tujuan yang harus dicapai pada tahun 2030. Tujuan-tujuan tersebut mencakup berbagai aspek kehidupan masyarakat desa, mulai dari pengentasan kemiskinan, peningkatan kesehatan dan pendidikan, kesetaraan gender, pertumbuhan ekonomi, infrastruktur, hingga kelestarian lingkungan. Adapun SDGs Desa terdiri dari tujuan-tujuan sebagai berikut:
|
||||||
</Text>
|
</Text>
|
||||||
<Center>
|
|
||||||
<Image src={"/api/img/sdgsdesa-18-removebg.png"} alt='' w={800} />
|
|
||||||
</Center>
|
|
||||||
</Box >
|
</Box >
|
||||||
<Box py={20}>
|
<Box py={20} px={{ base: "md", md: 100 }}>
|
||||||
|
<Box pos="relative" style={{ minHeight: 200 }}>
|
||||||
|
<LoadingOverlay visible={loading} overlayProps={{ blur: 2 }} />
|
||||||
|
{!loading && sdgsDesa.length > 0 ? (
|
||||||
|
<SimpleGrid
|
||||||
|
cols={{ base: 1, sm: 2, md: 3, lg: 4 }}
|
||||||
|
spacing="xl"
|
||||||
|
verticalSpacing="xl"
|
||||||
|
>
|
||||||
|
{sdgsDesa.map((item) => (
|
||||||
|
<Paper
|
||||||
|
key={item.id}
|
||||||
|
p="md"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '100%',
|
||||||
|
transition: 'transform 0.2s',
|
||||||
|
'&:hover': {
|
||||||
|
transform: 'translateY(-5px)',
|
||||||
|
boxShadow: '0 8px 16px rgba(0,0,0,0.1)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
p="md"
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#f8f9fa',
|
||||||
|
borderRadius: '8px',
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
marginBottom: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={item.image?.link || '/placeholder-sdgs.png'}
|
||||||
|
alt={item.name}
|
||||||
|
width={120}
|
||||||
|
height={120}
|
||||||
|
fit="contain"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Stack gap="xs" style={{ width: '100%' }}>
|
||||||
|
<Title order={4} ta="center" c={"dimmed"} fw={600} lineClamp={2} style={{ minHeight: '3rem' }}>
|
||||||
|
{item.name}
|
||||||
|
</Title>
|
||||||
|
<Text ta="center" fw={"bold"} c={colors['blue-button']} fz={"h2"} lineClamp={3} style={{ minHeight: '4.5rem' }}>
|
||||||
|
{item.jumlah}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
) : !loading ? (
|
||||||
|
<Center style={{ minHeight: 200 }}>
|
||||||
|
<Text>Tidak ada data SDGs Desa yang tersedia</Text>
|
||||||
|
</Center>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack >
|
</Stack >
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ function Footer() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Center>
|
</Center>
|
||||||
<Box py={20}>
|
<Box py={20} >
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
p={20}
|
p={20}
|
||||||
cols={{
|
cols={{
|
||||||
@@ -123,7 +123,7 @@ function Footer() {
|
|||||||
color: "white"
|
color: "white"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box p={mobile ? 30 : 30}>
|
<Box p={mobile ? 30 : 30} style={{color: "white"}}>
|
||||||
<Stack justify='space-between'>
|
<Stack justify='space-between'>
|
||||||
<Text fz={"md"} fw={"bold"}>Tentang Darmasaba</Text>
|
<Text fz={"md"} fw={"bold"}>Tentang Darmasaba</Text>
|
||||||
<Text fz={"xs"} >Desa Darmasaba adalah desa
|
<Text fz={"xs"} >Desa Darmasaba adalah desa
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import images from "@/con/images";
|
|
||||||
import { ActionIcon, Flex, Image } from "@mantine/core";
|
import { ActionIcon, Flex, Image } from "@mantine/core";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { useTransitionRouter } from "next-view-transitions";
|
||||||
|
|
||||||
function SosmedView() {
|
|
||||||
const listSosmed = Object.values(images.sosmed);
|
|
||||||
|
function SosmedView({data} : {data : Prisma.MediaSosialGetPayload<{ include: { image: true } }>[]}) {
|
||||||
|
const router = useTransitionRouter();
|
||||||
return (
|
return (
|
||||||
<Flex gap={"md"} justify={"center"} align={"center"}>
|
<Flex gap={"md"} justify={"center"} align={"center"}>
|
||||||
{listSosmed.map((item, k) => {
|
{data?.map((item, k) => {
|
||||||
return (
|
return (
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="transparent"
|
variant="transparent"
|
||||||
@@ -13,8 +16,11 @@ function SosmedView() {
|
|||||||
w={32}
|
w={32}
|
||||||
h={32}
|
h={32}
|
||||||
pos={"relative"}
|
pos={"relative"}
|
||||||
|
onClick={() => {
|
||||||
|
router.push(item.iconUrl || "");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Image src={item} alt="icon" loading="lazy" />
|
<Image src={item.image?.link || ""} alt="icon" loading="lazy" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Card,
|
Card,
|
||||||
|
Skeleton,
|
||||||
Flex,
|
Flex,
|
||||||
Grid,
|
Grid,
|
||||||
GridCol,
|
GridCol,
|
||||||
@@ -11,9 +13,9 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Text
|
Text
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import ModuleView from "./ModuleView";
|
import ModuleView from "./ModuleView";
|
||||||
import SosmedView from "./SosmedView";
|
import SosmedView from "./SosmedView";
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
const getDayOfWeek = () => {
|
const getDayOfWeek = () => {
|
||||||
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
|
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
|
||||||
@@ -56,6 +58,38 @@ const getWorkStatus = (day: string, currentTime: string): { status: string; mess
|
|||||||
|
|
||||||
|
|
||||||
function LandingPage() {
|
function LandingPage() {
|
||||||
|
const [socialMedia, setSocialMedia] = useState<Prisma.MediaSosialGetPayload<{ include: { image: true } }>[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSocialMedia = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/landingpage/mediasosial/findMany');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
// Ensure the data is an array before setting it
|
||||||
|
if (Array.isArray(result.data)) {
|
||||||
|
setSocialMedia(result.data);
|
||||||
|
} else if (Array.isArray(result)) {
|
||||||
|
// In case the API returns the array directly
|
||||||
|
setSocialMedia(result);
|
||||||
|
} else {
|
||||||
|
console.error('Unexpected API response format:', result);
|
||||||
|
setSocialMedia([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching social media:', error);
|
||||||
|
setSocialMedia([]); // Ensure we always have an array
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchSocialMedia();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const [workStatus, setWorkStatus] = useState<{ status: string; message: string }>
|
const [workStatus, setWorkStatus] = useState<{ status: string; message: string }>
|
||||||
({ status: '', message: '' });
|
({ status: '', message: '' });
|
||||||
|
|
||||||
@@ -248,7 +282,13 @@ function LandingPage() {
|
|||||||
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<ModuleView />
|
<ModuleView />
|
||||||
<SosmedView />
|
{isLoading ? (
|
||||||
|
<Skeleton height={32} width="100%" />
|
||||||
|
) : socialMedia.length > 0 ? (
|
||||||
|
<SosmedView data={socialMedia} />
|
||||||
|
) : (
|
||||||
|
<div>No social media links available</div>
|
||||||
|
)}
|
||||||
<Text c={colors.trans.dark[2]} style={{
|
<Text c={colors.trans.dark[2]} style={{
|
||||||
textAlign: "center"
|
textAlign: "center"
|
||||||
}} >Sampaikan saran dan masukan guna kemajuan dalam pembangunan. Semua lebih mudah melalui fitur interaktif</Text>
|
}} >Sampaikan saran dan masukan guna kemajuan dalam pembangunan. Semua lebih mudah melalui fitur interaktif</Text>
|
||||||
|
|||||||
@@ -1,12 +1,51 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
import { Box, Button, Center, Container, Image, Paper, SimpleGrid, Stack, Text, useMantineTheme } from "@mantine/core";
|
import { Box, Button, Center, Container, Image, Paper, SimpleGrid, Stack, Text, Title, useMantineTheme } from "@mantine/core";
|
||||||
import { useMediaQuery } from "@mantine/hooks";
|
import { useMediaQuery } from "@mantine/hooks";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function SDGS() {
|
export default function SDGS() {
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
const mobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`);
|
const mobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`);
|
||||||
|
const [sdgsDesa, setSdgsDesa] = useState<Prisma.SDGSDesaGetPayload<{ include: { image: true } }>[] | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSdgsDesa = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/landingpage/sdgsdesa/findMany');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
// Ensure the data is an array before setting it
|
||||||
|
let data = [];
|
||||||
|
if (Array.isArray(result.data)) {
|
||||||
|
data = result.data;
|
||||||
|
} else if (Array.isArray(result)) {
|
||||||
|
// In case the API returns the array directly
|
||||||
|
data = result;
|
||||||
|
} else {
|
||||||
|
console.error('Unexpected API response format:', result);
|
||||||
|
setSdgsDesa([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by jumlah in descending order and take top 3
|
||||||
|
const top3Sdgs = [...data]
|
||||||
|
.sort((a, b) => parseInt(b.jumlah) - parseInt(a.jumlah))
|
||||||
|
.slice(0, 3);
|
||||||
|
|
||||||
|
setSdgsDesa(top3Sdgs);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching sdgs desa:', error);
|
||||||
|
setSdgsDesa([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchSdgsDesa();
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Stack p={"sm"}>
|
<Stack p={"sm"}>
|
||||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"}>
|
<Container w={{ base: "100%", md: "80%" }} p={"xl"}>
|
||||||
@@ -16,22 +55,82 @@ export default function SDGS() {
|
|||||||
<Text fz={"1.4rem"} ta={"center"}>SDGs Desa adalah upaya menerapkan 17 Tujuan Pembangunan Berkelanjutan di tingkat desa.
|
<Text fz={"1.4rem"} ta={"center"}>SDGs Desa adalah upaya menerapkan 17 Tujuan Pembangunan Berkelanjutan di tingkat desa.
|
||||||
Dengan fokus pada pengentasan kemiskinan, pendidikan, kesehatan, kesetaraan gender, dan pelestarian lingkungan, kami berkomitmen untuk menciptakan desa yang lebih baik bagi semua</Text>
|
Dengan fokus pada pengentasan kemiskinan, pendidikan, kesehatan, kesetaraan gender, dan pelestarian lingkungan, kami berkomitmen untuk menciptakan desa yang lebih baik bagi semua</Text>
|
||||||
<Box py={50}>
|
<Box py={50}>
|
||||||
<Paper p={"lg"} bg={colors.Bg}>
|
<Paper p={{ base: 'md', md: 'xl' }} bg={colors.Bg} radius="lg" shadow="sm">
|
||||||
<SimpleGrid
|
{sdgsDesa && sdgsDesa.length > 0 ? (
|
||||||
cols={{
|
<SimpleGrid
|
||||||
base: 1,
|
cols={{ base: 1, sm: 3 }}
|
||||||
sm: 3,
|
spacing="xl"
|
||||||
}}>
|
verticalSpacing="xl"
|
||||||
<Center>
|
>
|
||||||
<Image src={"/api/img/sgdesa-1.png"} alt="" w={mobile ? 250 : 200} />
|
{sdgsDesa.map((item) => (
|
||||||
</Center>
|
<Box
|
||||||
<Center>
|
key={item.id}
|
||||||
<Image src={"/api/img/sgdesa-2.png"} alt="" w={mobile ? 250 : 220} />
|
p="md"
|
||||||
</Center>
|
style={{
|
||||||
<Center>
|
display: 'flex',
|
||||||
<Image src={"/api/img/sgdesa-3.png"} alt="" w={mobile ? 250 : 190} />
|
flexDirection: 'column',
|
||||||
</Center>
|
alignItems: 'center',
|
||||||
</SimpleGrid>
|
justifyContent: 'center',
|
||||||
|
height: '100%',
|
||||||
|
transition: 'transform 0.2s',
|
||||||
|
'&:hover': {
|
||||||
|
transform: 'translateY(-5px)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
p="md"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'white',
|
||||||
|
width: mobile ? 150 : 180,
|
||||||
|
height: mobile ? 150 : 180,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
marginBottom: '1.5rem',
|
||||||
|
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={item.image?.link ? item.image.link : '/placeholder-sdgs.png'}
|
||||||
|
alt={item.name}
|
||||||
|
width={mobile ? 100 : 120}
|
||||||
|
height={mobile ? 100 : 120}
|
||||||
|
style={{
|
||||||
|
objectFit: 'contain',
|
||||||
|
maxWidth: '100%',
|
||||||
|
maxHeight: '100%'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: 'lg', md: 'xl' }}
|
||||||
|
fw={700}
|
||||||
|
mb="xs"
|
||||||
|
style={{ lineHeight: 1.2 }}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
<Title
|
||||||
|
order={2}
|
||||||
|
ta="center"
|
||||||
|
c={colors['blue-button']}
|
||||||
|
style={{
|
||||||
|
fontSize: mobile ? '2.5rem' : '3rem',
|
||||||
|
lineHeight: 1,
|
||||||
|
margin: '0.5rem 0',
|
||||||
|
fontWeight: 800
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.jumlah}
|
||||||
|
</Title>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
) : (
|
||||||
|
<Text>Tidak ada data SDGs Desa</Text>
|
||||||
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
<Center>
|
<Center>
|
||||||
<Button component={Link} href={"/darmasaba/sdgs-desa"} radius={"lg"} fz={"1.2rem"} mt={20} bg={colors["blue-button"]}>Selengkapnya</Button>
|
<Button component={Link} href={"/darmasaba/sdgs-desa"} radius={"lg"} fz={"1.2rem"} mt={20} bg={colors["blue-button"]}>Selengkapnya</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user