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())
|
||||
name String @unique
|
||||
jumlah String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
|
||||
@@ -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";
|
||||
@@ -52,19 +53,38 @@ const sdgsDesa = proxy({
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<
|
||||
Prisma.SDGSDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>
|
||||
> | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.landingpage.sdgsdesa[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
sdgsDesa.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
|
||||
sdgsDesa.findMany.loading = true; // Use the full path to access the property
|
||||
sdgsDesa.findMany.page = page;
|
||||
try {
|
||||
const res = await ApiFetch.api.landingpage.sdgsdesa[
|
||||
"findMany"
|
||||
].get({
|
||||
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) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton height={300} />
|
||||
<Skeleton height={550} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton height={300} />
|
||||
<Skeleton height={550} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ function ListMediaSosial({ search }: { search: string }) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton height={300} />
|
||||
<Skeleton height={550} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ function ListProgramInovasi({ search }: { search: string }) {
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton height={300} />
|
||||
<Skeleton height={550} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ function EditKolaborasiInovasi() {
|
||||
<iframe
|
||||
src={previewImage}
|
||||
width="100%"
|
||||
height="500px"
|
||||
height="250px"
|
||||
style={{ border: "1px solid #ccc", borderRadius: "8px" }}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -110,7 +110,7 @@ function CreateSDGsDesa() {
|
||||
<iframe
|
||||
src={previewImage}
|
||||
width="100%"
|
||||
height="500px"
|
||||
height="250px"
|
||||
style={{ border: "1px solid #ccc", borderRadius: "8px" }}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -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 sdgsDesa from '../../_state/landing-page/sdgs-desa';
|
||||
@@ -30,24 +30,61 @@ function SdgsDesa() {
|
||||
function ListSdgsDesa({ search }: { search: string }) {
|
||||
const listState = useProxy(sdgsDesa)
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
totalPages,
|
||||
loading,
|
||||
load,
|
||||
} = listState.findMany;
|
||||
|
||||
useEffect(() => {
|
||||
listState.findMany.load()
|
||||
load(page, 10)
|
||||
}, [])
|
||||
|
||||
const filteredData = (listState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.jumlah.toLowerCase().includes(keyword)
|
||||
)
|
||||
});
|
||||
const filteredData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return data.filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(keyword) ||
|
||||
item.jumlah?.toLowerCase().includes(keyword)
|
||||
);
|
||||
})
|
||||
}, [data, search]);
|
||||
|
||||
if (!listState.findMany.data) {
|
||||
// Handle loading state
|
||||
if (loading || !data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
<Skeleton height={550} />
|
||||
</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 (
|
||||
@@ -90,6 +127,18 @@ function ListSdgsDesa({ 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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ async function sdgsDesaFindMany(context: Context) {
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
orderBy: { jumlah: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.sDGSDesa.count({
|
||||
where: { isActive: true },
|
||||
|
||||
@@ -11,7 +11,7 @@ const SDGSDesa = new Elysia({
|
||||
})
|
||||
|
||||
// ✅ Find all
|
||||
.get("/find-many", sdgsDesaFindMany)
|
||||
.get("/findMany", sdgsDesaFindMany)
|
||||
|
||||
// ✅ Find by ID
|
||||
.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 colors from '@/con/colors';
|
||||
|
||||
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}>
|
||||
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
|
||||
<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:
|
||||
</Text>
|
||||
<Center>
|
||||
<Image src={"/api/img/sdgsdesa-18-removebg.png"} alt='' w={800} />
|
||||
</Center>
|
||||
</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>
|
||||
</Stack >
|
||||
);
|
||||
|
||||
@@ -112,7 +112,7 @@ function Footer() {
|
||||
</Box>
|
||||
</Paper>
|
||||
</Center>
|
||||
<Box py={20}>
|
||||
<Box py={20} >
|
||||
<SimpleGrid
|
||||
p={20}
|
||||
cols={{
|
||||
@@ -123,7 +123,7 @@ function Footer() {
|
||||
color: "white"
|
||||
}}
|
||||
>
|
||||
<Box p={mobile ? 30 : 30}>
|
||||
<Box p={mobile ? 30 : 30} style={{color: "white"}}>
|
||||
<Stack justify='space-between'>
|
||||
<Text fz={"md"} fw={"bold"}>Tentang Darmasaba</Text>
|
||||
<Text fz={"xs"} >Desa Darmasaba adalah desa
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import images from "@/con/images";
|
||||
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 (
|
||||
<Flex gap={"md"} justify={"center"} align={"center"}>
|
||||
{listSosmed.map((item, k) => {
|
||||
{data?.map((item, k) => {
|
||||
return (
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
@@ -13,8 +16,11 @@ function SosmedView() {
|
||||
w={32}
|
||||
h={32}
|
||||
pos={"relative"}
|
||||
onClick={() => {
|
||||
router.push(item.iconUrl || "");
|
||||
}}
|
||||
>
|
||||
<Image src={item} alt="icon" loading="lazy" />
|
||||
<Image src={item.image?.link || ""} alt="icon" loading="lazy" />
|
||||
</ActionIcon>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
"use client";
|
||||
import colors from "@/con/colors";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
Skeleton,
|
||||
Flex,
|
||||
Grid,
|
||||
GridCol,
|
||||
@@ -11,9 +13,9 @@ import {
|
||||
Stack,
|
||||
Text
|
||||
} from "@mantine/core";
|
||||
import { useEffect, useState } from "react";
|
||||
import ModuleView from "./ModuleView";
|
||||
import SosmedView from "./SosmedView";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const getDayOfWeek = () => {
|
||||
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
|
||||
@@ -56,6 +58,38 @@ const getWorkStatus = (day: string, currentTime: string): { status: string; mess
|
||||
|
||||
|
||||
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 }>
|
||||
({ status: '', message: '' });
|
||||
|
||||
@@ -248,7 +282,13 @@ function LandingPage() {
|
||||
|
||||
</Flex>
|
||||
<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={{
|
||||
textAlign: "center"
|
||||
}} >Sampaikan saran dan masukan guna kemajuan dalam pembangunan. Semua lebih mudah melalui fitur interaktif</Text>
|
||||
|
||||
@@ -1,12 +1,51 @@
|
||||
'use client'
|
||||
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 { Prisma } from "@prisma/client";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function SDGS() {
|
||||
const theme = useMantineTheme();
|
||||
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 (
|
||||
<Stack p={"sm"}>
|
||||
<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.
|
||||
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}>
|
||||
<Paper p={"lg"} bg={colors.Bg}>
|
||||
<SimpleGrid
|
||||
cols={{
|
||||
base: 1,
|
||||
sm: 3,
|
||||
}}>
|
||||
<Center>
|
||||
<Image src={"/api/img/sgdesa-1.png"} alt="" w={mobile ? 250 : 200} />
|
||||
</Center>
|
||||
<Center>
|
||||
<Image src={"/api/img/sgdesa-2.png"} alt="" w={mobile ? 250 : 220} />
|
||||
</Center>
|
||||
<Center>
|
||||
<Image src={"/api/img/sgdesa-3.png"} alt="" w={mobile ? 250 : 190} />
|
||||
</Center>
|
||||
</SimpleGrid>
|
||||
<Paper p={{ base: 'md', md: 'xl' }} bg={colors.Bg} radius="lg" shadow="sm">
|
||||
{sdgsDesa && sdgsDesa.length > 0 ? (
|
||||
<SimpleGrid
|
||||
cols={{ base: 1, sm: 3 }}
|
||||
spacing="xl"
|
||||
verticalSpacing="xl"
|
||||
>
|
||||
{sdgsDesa.map((item) => (
|
||||
<Box
|
||||
key={item.id}
|
||||
p="md"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
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>
|
||||
<Center>
|
||||
<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