Sinkronisasi UI & API Admin - User Submenu Penghargaan

This commit is contained in:
2025-08-06 11:56:21 +08:00
parent b62c4be30a
commit d4af56b508
15 changed files with 491 additions and 428 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";
@@ -5,10 +6,10 @@ import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
name: z.string().min(1).max(50),
juara: z.string().min(1).max(50),
name: z.string().min(1).max(5000),
juara: z.string().min(1).max(5000),
deskripsi: z.string().min(1).max(5000),
imageId: z.string().min(1).max(50),
imageId: z.string().min(1).max(5000),
});
const defaultForm = {
@@ -50,17 +51,38 @@ const penghargaanState = proxy({
},
},
findMany: {
data: null as
| Prisma.PenghargaanGetPayload<{
include: {
image: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.desa.penghargaan["find-many"].get();
if (res.status === 200) {
penghargaanState.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
penghargaanState.findMany.loading = true; // Use the full path to access the property
penghargaanState.findMany.page = page;
try {
const res = await ApiFetch.api.desa.penghargaan[
"find-many"
].get({
query: { page, limit },
});
if (res.status === 200 && res.data?.success) {
penghargaanState.findMany.data = res.data.data || [];
penghargaanState.findMany.total = res.data.total || 0;
penghargaanState.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load penghargaan:", res.data?.message);
penghargaanState.findMany.data = [];
penghargaanState.findMany.total = 0;
penghargaanState.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading penghargaan:", error);
penghargaanState.findMany.data = [];
penghargaanState.findMany.total = 0;
penghargaanState.findMany.totalPages = 1;
} finally {
penghargaanState.findMany.loading = false;
}
},
},

View File

@@ -51,7 +51,6 @@ function ListSuratKeterangan({ search }: { search: string }) {
item.deskripsi?.toLowerCase().includes(keyword)
);
})
.sort((a, b) => a.posisi?.hierarki - b.posisi?.hierarki);
}, [data, search]);
// Handle loading state

View File

@@ -50,6 +50,10 @@ function DetailPenghargaan() {
{statePenghargaan.findUnique.data ? (
<Paper key={statePenghargaan.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 400, md: 400, lg: 400 }} src={statePenghargaan.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Judul</Text>
<Text fz={"lg"}>{statePenghargaan.findUnique.data?.name}</Text>
@@ -62,10 +66,6 @@ function DetailPenghargaan() {
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: statePenghargaan.findUnique.data?.deskripsi }} />
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 150, lg: 150 }} src={statePenghargaan.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Flex gap={"xs"} mt={10}>
<Button
onClick={() => {

View File

@@ -1,61 +1,103 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import penghargaanState from '@/app/admin/(dashboard)/_state/desa/penghargaan';
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } 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 { useEffect, useMemo, useState } from 'react';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../_com/judulListTab';
import { useState } from 'react';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
function Penghargaan() {
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='Posisi Organisasi'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPenghargaan search={search} />
</Box>
);
const [search, setSearch] = useState("");
return (
<Box>
<HeaderSearch
title='Penghargaan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPenghargaan search={search} />
</Box>
);
}
function ListPenghargaan({ search }: { search: string }) {
const state = useProxy(penghargaanState)
const router = useRouter()
useShallowEffect(() => {
state.findMany.load()
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
useEffect(() => {
load(page, 10)
}, [])
const filteredData = (state.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.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)
);
})
}, [data, search]);
if (!state.findMany.data) {
return(
// 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 Penghargaan'
href='/admin/desa/penghargaan/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd colSpan={4}>
<Text ta="center">Tidak ada data</Text>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Paper>
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
<JudulList
title='List Penghargaan'
href='/admin/desa/penghargaan/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
@@ -69,25 +111,43 @@ function ListPenghargaan({ search }: { search: string }) {
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
<Box w={100}>
<Text lineClamp={1} truncate="end" fz={"sm"}>{item.name}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={100}>
<Text lineClamp={1} truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="gambar" />
</TableTd>
</TableTd>
<TableTd>
<Text>
<Button onClick={() => router.push(`/admin/desa/penghargaan/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</Text>
</TableTd>
</TableTr>
))}
<IconDeviceImac size={20} />
</Button>
</Text>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</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,23 +1,46 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function penghargaanFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const skip = (page - 1) * limit;
export default async function penghargaanFindMany() {
try {
const data = await prisma.penghargaan.findMany({
include: {
image: true,
},
});
const [data, total] = await Promise.all([
prisma.penghargaan.findMany({
where: { isActive: true },
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.penghargaan.count({
where: { isActive: true }
})
]);
const totalPages = Math.ceil(total / limit);
return {
success: true,
message: "Success fetch penghargaan",
message: "Success fetch penghargaan with pagination",
data,
page,
totalPages,
total,
};
} catch (error) {
console.error("Find many error:", error);
} catch (e) {
console.error("Find many paginated error:", e);
return {
success: false,
message: "Failed fetch penghargaan",
message: "Failed fetch penghargaan with pagination",
data: [],
page: 1,
totalPages: 1,
total: 0,
};
}
}
}

View File

@@ -1,96 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Stack, Container, Text, Image, ActionIcon, Box, Divider, Flex, Group } from '@mantine/core';
import { IconBrandFacebook, IconBrandInstagram, IconBrandTwitter, IconBrandWhatsapp } from '@tabler/icons-react';
import React from 'react';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={22}>
<Group px={{ base: "md", md: 100 }}>
<BackButton />
</Group>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"}>
Juara 2
</Text>
<Text
ta={"center"}
fw={"bold"}
>
Duta Invenstasi Kabupaten Badung 2024
</Text>
<Image py={20} src={"/api/img/penghargaan-3.png"} alt='' />
</Stack>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Text
pb={20}
ta={"center"}
fw={"bold"}
>
Desa Dramasaba Raih Juara 2 dalam Duta Invenstasi Kabupaten Badung 2024
</Text>
<Text
ta={"justify"}
>
Desa Dramasaba kembali menunjukkan eksistensinya dalam memajukan potensi desa dengan meraih Juara 2 dalam ajang Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024. Prestasi ini menjadi bukti nyata bahwa Desa Dramasaba memiliki daya tarik serta peluang investasi yang menjanjikan bagi para investor maupun masyarakat yang ingin berkontribusi dalam pengembangan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dalam video pendek yang diikutsertakan dalam lomba ini, Desa Dramasaba menampilkan berbagai potensi unggulan yang dimiliki, mulai dari sektor pertanian, pariwisata berbasis budaya, hingga ekonomi kreatif yang sedang berkembang pesat. Dengan pengemasan yang menarik dan penyampaian informasi yang jelas, video tersebut berhasil memikat perhatian dewan juri serta menjadi inspirasi bagi desa-desa lain dalam mengembangkan potensi wilayahnya masing-masing.
</Text>
<Text
ta={"justify"}
>
Keberhasilan ini tidak lepas dari kerja keras seluruh tim kreatif, pemerintah desa, serta masyarakat yang turut berpartisipasi dalam produksi video. Kepala Desa Dramasaba, [Nama Kepala Desa], mengungkapkan rasa syukur dan bangganya atas pencapaian ini. Menurutnya, kemenangan ini bukan sekadar penghargaan, tetapi juga menjadi motivasi untuk terus mengembangkan desa agar semakin dikenal luas dan menarik lebih banyak peluang investasi.
</Text>
<Text
py={5}
ta={"justify"}
>
Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024 merupakan ajang yang diadakan oleh pemerintah daerah dengan tujuan untuk mendorong desa-desa dalam mengenalkan dan mempromosikan potensi investasi mereka. Dengan mengikuti lomba ini, Desa Dramasaba tidak hanya memperoleh apresiasi, tetapi juga membuka peluang lebih besar untuk berkolaborasi dengan investor maupun pihak lain yang tertarik dalam pengembangan desa.
</Text>
<Text
ta={"justify"}
>
Ke depan, Desa Dramasaba berkomitmen untuk terus meningkatkan daya saingnya, baik dalam sektor ekonomi, budaya, maupun infrastruktur, sehingga mampu memberikan manfaat yang lebih besar bagi masyarakat serta menarik perhatian lebih banyak pihak untuk berinvestasi dan berkontribusi dalam pembangunan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dengan prestasi ini, diharapkan Desa Dramasaba dapat menjadi contoh bagi desa-desa lain dalam memanfaatkan media digital sebagai sarana promosi dan pengembangan potensi lokal.
</Text>
<Box py={20}>
<Divider />
<Flex justify={"space-between"} py={20}>
<Text fz={"sm"}>25 May 2021 . Darmasaba</Text>
<Box>
<Flex gap={"lg"}>
<ActionIcon variant='transparent'>
<IconBrandFacebook color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandInstagram color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandTwitter color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandWhatsapp color={"gray"} size={"30"} />
</ActionIcon>
</Flex>
</Box>
</Flex>
<Divider pb={50} />
</Box>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,97 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Stack, Container, Text, Image, ActionIcon, Box, Divider, Flex, Group } from '@mantine/core';
import { IconBrandFacebook, IconBrandInstagram, IconBrandTwitter, IconBrandWhatsapp } from '@tabler/icons-react';
import React from 'react';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={22}>
<Group px={{ base: "md", md: 100 }}>
<BackButton />
</Group>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"}>
Juara 2
</Text>
<Text
ta={"center"}
fw={"bold"}
>
Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024
</Text>
<Image py={20} src={"/api/img/penghargaan-2.png"} alt='' />
</Stack>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Text
pb={20}
ta={"center"}
fw={"bold"}
>
Desa Dramasaba Raih Juara 2 dalam Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024
</Text>
<Text
ta={"justify"}
>
Desa Dramasaba kembali menunjukkan eksistensinya dalam memajukan potensi desa dengan meraih Juara 2 dalam ajang Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024. Prestasi ini menjadi bukti nyata bahwa Desa Dramasaba memiliki daya tarik serta peluang investasi yang menjanjikan bagi para investor maupun masyarakat yang ingin berkontribusi dalam pengembangan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dalam video pendek yang diikutsertakan dalam lomba ini, Desa Dramasaba menampilkan berbagai potensi unggulan yang dimiliki, mulai dari sektor pertanian, pariwisata berbasis budaya, hingga ekonomi kreatif yang sedang berkembang pesat. Dengan pengemasan yang menarik dan penyampaian informasi yang jelas, video tersebut berhasil memikat perhatian dewan juri serta menjadi inspirasi bagi desa-desa lain dalam mengembangkan potensi wilayahnya masing-masing.
</Text>
<Text
ta={"justify"}
>
Keberhasilan ini tidak lepas dari kerja keras seluruh tim kreatif, pemerintah desa, serta masyarakat yang turut berpartisipasi dalam produksi video. Kepala Desa Dramasaba, [Nama Kepala Desa], mengungkapkan rasa syukur dan bangganya atas pencapaian ini. Menurutnya, kemenangan ini bukan sekadar penghargaan, tetapi juga menjadi motivasi untuk terus mengembangkan desa agar semakin dikenal luas dan menarik lebih banyak peluang investasi.
</Text>
<Text
py={5}
ta={"justify"}
>
Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024 merupakan ajang yang diadakan oleh pemerintah daerah dengan tujuan untuk mendorong desa-desa dalam mengenalkan dan mempromosikan potensi investasi mereka. Dengan mengikuti lomba ini, Desa Dramasaba tidak hanya memperoleh apresiasi, tetapi juga membuka peluang lebih besar untuk berkolaborasi dengan investor maupun pihak lain yang tertarik dalam pengembangan desa.
</Text>
<Text
ta={"justify"}
>
Ke depan, Desa Dramasaba berkomitmen untuk terus meningkatkan daya saingnya, baik dalam sektor ekonomi, budaya, maupun infrastruktur, sehingga mampu memberikan manfaat yang lebih besar bagi masyarakat serta menarik perhatian lebih banyak pihak untuk berinvestasi dan berkontribusi dalam pembangunan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dengan prestasi ini, diharapkan Desa Dramasaba dapat menjadi contoh bagi desa-desa lain dalam memanfaatkan media digital sebagai sarana promosi dan pengembangan potensi lokal.
</Text>
<Box py={20}>
<Divider />
<Flex justify={"space-between"} py={20}>
<Text fz={"sm"}>25 May 2021 . Darmasaba</Text>
<Box>
<Flex gap={"lg"}>
<ActionIcon variant='transparent'>
<IconBrandFacebook color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandInstagram color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandTwitter color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandWhatsapp color={"gray"} size={"30"} />
</ActionIcon>
</Flex>
</Box>
</Flex>
<Divider pb={50} />
</Box>
</Box>
</Stack >
);
}
export default Page;

View File

@@ -1,97 +0,0 @@
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Stack, Container, Text, Image, ActionIcon, Box, Divider, Flex } from '@mantine/core';
import { IconBrandFacebook, IconBrandInstagram, IconBrandTwitter, IconBrandWhatsapp } from '@tabler/icons-react';
import React from 'react';
function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={22}>
<Box px={{ base: "md", md: 100 }}>
<BackButton />
</Box>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text fz={"3.4rem"} fw={"bold"}>
Juara Favorit
</Text>
<Text
ta={"center"}
fw={"bold"}
>
Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024
</Text>
<Image py={20} src={"/api/img/penghargaan-1.png"} alt='' />
</Stack>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Text
pb={20}
ta={"center"}
fw={"bold"}
>
Desa Dramasaba Raih Juara Favorit dalam Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024
</Text>
<Text
ta={"justify"}
>
Desa Dramasaba kembali menunjukkan eksistensinya dalam memajukan potensi desa dengan meraih Juara 2 dalam ajang Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024. Prestasi ini menjadi bukti nyata bahwa Desa Dramasaba memiliki daya tarik serta peluang investasi yang menjanjikan bagi para investor maupun masyarakat yang ingin berkontribusi dalam pengembangan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dalam video pendek yang diikutsertakan dalam lomba ini, Desa Dramasaba menampilkan berbagai potensi unggulan yang dimiliki, mulai dari sektor pertanian, pariwisata berbasis budaya, hingga ekonomi kreatif yang sedang berkembang pesat. Dengan pengemasan yang menarik dan penyampaian informasi yang jelas, video tersebut berhasil memikat perhatian dewan juri serta menjadi inspirasi bagi desa-desa lain dalam mengembangkan potensi wilayahnya masing-masing.
</Text>
<Text
ta={"justify"}
>
Keberhasilan ini tidak lepas dari kerja keras seluruh tim kreatif, pemerintah desa, serta masyarakat yang turut berpartisipasi dalam produksi video. Kepala Desa Dramasaba, [Nama Kepala Desa], mengungkapkan rasa syukur dan bangganya atas pencapaian ini. Menurutnya, kemenangan ini bukan sekadar penghargaan, tetapi juga menjadi motivasi untuk terus mengembangkan desa agar semakin dikenal luas dan menarik lebih banyak peluang investasi.
</Text>
<Text
py={5}
ta={"justify"}
>
Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024 merupakan ajang yang diadakan oleh pemerintah daerah dengan tujuan untuk mendorong desa-desa dalam mengenalkan dan mempromosikan potensi investasi mereka. Dengan mengikuti lomba ini, Desa Dramasaba tidak hanya memperoleh apresiasi, tetapi juga membuka peluang lebih besar untuk berkolaborasi dengan investor maupun pihak lain yang tertarik dalam pengembangan desa.
</Text>
<Text
ta={"justify"}
>
Ke depan, Desa Dramasaba berkomitmen untuk terus meningkatkan daya saingnya, baik dalam sektor ekonomi, budaya, maupun infrastruktur, sehingga mampu memberikan manfaat yang lebih besar bagi masyarakat serta menarik perhatian lebih banyak pihak untuk berinvestasi dan berkontribusi dalam pembangunan desa.
</Text>
<Text
py={5}
ta={"justify"}
>
Dengan prestasi ini, diharapkan Desa Dramasaba dapat menjadi contoh bagi desa-desa lain dalam memanfaatkan media digital sebagai sarana promosi dan pengembangan potensi lokal.
</Text>
<Box py={20}>
<Divider />
<Flex justify={"space-between"} py={20}>
<Text fz={"sm"}>25 May 2021 . Darmasaba</Text>
<Box>
<Flex gap={"lg"}>
<ActionIcon variant='transparent'>
<IconBrandFacebook color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandInstagram color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandTwitter color={"gray"} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandWhatsapp color={"gray"} size={"30"} />
</ActionIcon>
</Flex>
</Box>
</Flex>
<Divider pb={50} />
</Box>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -0,0 +1,103 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors';
import { Stack, Container, Text, Image, ActionIcon, Box, Divider, Flex, Center, Skeleton } from '@mantine/core';
import { IconBrandFacebook, IconBrandInstagram, IconBrandTwitter, IconBrandWhatsapp } from '@tabler/icons-react';
import React, { useEffect } from 'react';
import { useParams } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import penghargaanState from '@/app/admin/(dashboard)/_state/desa/penghargaan';
import { useState } from 'react';
function Page() {
const params = useParams<{ id: string }>();
const id = Array.isArray(params.id) ? params.id[0] : params.id;
const state = useProxy(penghargaanState);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadData = async () => {
if (!id) return;
try {
setLoading(true);
await state.findUnique.load(id);
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
}
loadData()
}, [id])
if (loading) {
return (
<Center>
<Skeleton height={500} />
</Center>
);
}
if (!state.findUnique.data) {
return (
<Center>
<Text>Data tidak ditemukan</Text>
</Center>
);
}
return (
<Stack pos={"relative"} bg={colors['BG-trans']} py={"xl"} gap={22}>
<Box px={{ base: "md", md: 100 }}>
<BackButton />
</Box>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
<Text
ta={"center"}
fw={"bold"}
fz={"2.3rem"}
>
{state.findUnique.data?.name}
</Text>
<Image py={20} src={state.findUnique.data?.image?.link || ''} alt='' />
</Stack>
</Container>
<Box px={{ base: "md", md: 100 }}>
<Text
pb={20}
ta={"center"}
fw={"bold"}
dangerouslySetInnerHTML={{ __html: state.findUnique.data?.deskripsi || '' }}
/>
<Box py={20}>
<Divider color={colors['blue-button']} />
<Flex justify={"space-between"} py={20}>
<Text fz={"sm"}>{new Date(state.findUnique.data?.createdAt).toLocaleDateString()}</Text>
<Box>
<Flex gap={"lg"}>
<ActionIcon variant='transparent'>
<IconBrandFacebook color={colors['blue-button']} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandInstagram color={colors['blue-button']} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandTwitter color={colors['blue-button']} size={"30"} />
</ActionIcon>
<ActionIcon variant='transparent'>
<IconBrandWhatsapp color={colors['blue-button']} size={"30"} />
</ActionIcon>
</Flex>
</Box>
</Flex>
<Divider color={colors['blue-button']} pb={50} />
</Box>
</Box>
</Stack>
);
}
export default Page;

View File

@@ -1,63 +1,21 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
import penghargaanState from "@/app/admin/(dashboard)/_state/desa/penghargaan";
import colors from "@/con/colors";
import { Carousel, CarouselSlide } from "@mantine/carousel";
import { Box, Button, Container, Group, Paper, Stack, Text, useMantineTheme } from "@mantine/core";
import { useMediaQuery } from "@mantine/hooks";
import Autoplay from "embla-carousel-autoplay";
import Link from "next/link";
import { useRef } from "react";
import { useTransitionRouter } from "next-view-transitions";
import { useEffect, useRef } from "react";
import { useProxy } from "valtio/utils";
import BackButton from "../../(pages)/desa/layanan/_com/BackButto";
const data = [
{
id: 1,
images: "/api/img/penghargaan-2.png",
juara: "Juara 2",
deskripsi: "Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024",
link: "/darmasaba/penghargaan/juara-2-lomba-video-pendek-potensi-dan-peluang-investasi-di-desa-kabupaten-badung-2024"
},
{
id: 2,
images: "/api/img/penghargaan-3.png",
juara: "Juara 2",
deskripsi: "Duta Invenstasi Kabupaten Badung 2024",
link: "/darmasaba/penghargaan/juara-2-duta-invenstasi-kabupaten-badung-2024"
},
{
id: 3,
images: "/api/img/penghargaan-1.png",
juara: "Juara Favorit",
deskripsi: "Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024",
link: "/darmasaba/penghargaan/juara-favorit-lomba-video-pendek-potensi-dan-peluang-investasi-di-desa-kabupaten-badung-2024"
},
{
id: 4,
images: "/api/img/penghargaan-2.png",
juara: "Juara 2",
deskripsi: "Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024",
link: "/darmasaba/penghargaan/juara-2-lomba-video-pendek-potensi-dan-peluang-investasi-di-desa-kabupaten-badung-2024"
},
{
id: 5,
images: "/api/img/penghargaan-3.png",
juara: "Juara 2",
deskripsi: "Duta Invenstasi Kabupaten Badung 2024",
link: "/darmasaba/penghargaan/juara-2-duta-invenstasi-kabupaten-badung-2024"
},
{
id: 6,
images: "/api/img/penghargaan-1.png",
juara: "Juara Favorit",
deskripsi: "Lomba Video Pendek Potensi dan Peluang Investasi di Desa Kabupaten Badung 2024",
link: "/darmasaba/penghargaan/juara-favorit-lomba-video-pendek-potensi-dan-peluang-investasi-di-desa-kabupaten-badung-2024"
}
]
export default function Page() {
return (
<Stack pos={"relative"} bg={colors.grey[1]} py={"xl"} gap={22}>
<Box px={{ base: "md", md: 100 }}>
<BackButton />
<BackButton />
</Box>
<Container w={{ base: "100%", md: "50%" }}>
<Stack align="center" gap={0}>
@@ -82,12 +40,27 @@ function Slider() {
const theme = useMantineTheme();
const mobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`);
const autoplay = useRef(Autoplay({ delay: 2000 }));
const state = useProxy(penghargaanState);
const roter = useTransitionRouter()
useEffect(() => {
const loadData = async () => {
try {
await state.findMany.load();
} catch (error) {
console.error('Error loading data:', error);
}
}
loadData();
}, [])
const data = state.findMany.data || [];
const slides = data.map((item) => (
<CarouselSlide key={item.id}>
<Paper h={"100%"} pos={"relative"} style={{
backgroundImage: `url(${item.images}) `,
backgroundImage: `url(${item.image?.link}) `,
backgroundSize: "cover",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
@@ -104,11 +77,10 @@ function Slider() {
/>
<Stack justify="space-between" h={"100%"} gap={0} p={"lg"} pos={"relative"} >
<Box p={"lg"}>
<Text fz={"2.3rem"} fw={"bold"} ta={"center"} c={"white"}>{item.juara}</Text>
<Text fz={"1.5rem"} ta={"center"} c={"white"}>{item.deskripsi}</Text>
<Text fz={"1.5rem"} ta={"center"} c={"white"}>{item.name}</Text>
</Box>
<Group justify="center">
<Button component={Link} href={item.link} px={46} radius={"100"} size="md" bg={colors["blue-button"]}>
<Group justify="center" >
<Button onClick={() => roter.push(`/darmasaba/penghargaan/${item.id}`)} px={46} radius={"100"} size="md" bg={colors["blue-button"]}>
Detail
</Button>
</Group>
@@ -125,7 +97,7 @@ function Slider() {
plugins={[autoplay.current]}
onMouseEnter={autoplay.current.stop}
onMouseLeave={autoplay.current.reset}
w={{base: 500, md: 800, lg: 900 , xl: width}}
w={{ base: 500, md: 800, lg: 900, xl: width }}
height={height}
slideSize={{ base: "100%", sm: "50%", md: "33.333333%" }}
slideGap={{ base: "xl", sm: "md" }}

View File

@@ -1,10 +1,32 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
import penghargaanState from "@/app/admin/(dashboard)/_state/desa/penghargaan";
import colors from "@/con/colors";
import { Stack, Box, Container, Button, Text } from "@mantine/core";
import { useTransitionRouter } from 'next-view-transitions'
import { useEffect, useState } from "react";
import { useProxy } from "valtio/utils";
function Penghargaan() {
const router = useTransitionRouter()
const state = useProxy(penghargaanState)
const [loading, setLoading] = useState(false)
useEffect(() => {
const loadData = async () => {
try {
setLoading(true)
await state.findMany.load()
} catch (error) {
console.error('Error loading data:', error)
} finally {
setLoading(false)
}
}
loadData()
}, [])
const data = state.findMany.data?.slice(0, 3)
return (
<Stack pos={"relative"} h={720}>
<video
@@ -44,17 +66,30 @@ function Penghargaan() {
>
Penghargaan
</Text>
<Stack align="center" gap={0}>
<Text fz={"1.4rem"} c={"white"}>
Juara 2 Lomba Video Pendek
{loading ? (
<Text
style={{
textAlign: "center",
}}
fw={"bold"}
fz={"2.4rem"}
c={"white"}
>
Memuat Data...
</Text>
<Text fz={"1.4rem"} c={"white"}>
Juara 2 Duta Investasi
</Text>
<Text fz={"1.2rem"} c={"white"}>
Juara Favorit Lomba Video Pendek
</Text>
</Stack>
) : (
data?.map((v, k) => {
return (
<Box key={k}>
<Stack align="center" gap={0}>
<Text fz={"1.4rem"} c={"white"}>
{v.name}
</Text>
</Stack>
</Box>
);
})
)}
<Button color={colors["blue-button"]} onClick={() => router.push("/darmasaba/penghargaan")} variant="white" radius={100}>
Selanjutnya
</Button>