Fix QC Keano FrontEnd
Fix QC Kak Ayu Admin 29 Okt
This commit is contained in:
@@ -12,6 +12,7 @@ import BackButton from '../layanan/_com/BackButto';
|
||||
function Page() {
|
||||
const router = useTransitionRouter()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [hoveredId, setHoveredId] = useState<string | null>(null)
|
||||
const state = useProxy(potensiDesaState)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -88,20 +89,55 @@ function Page() {
|
||||
src={v.image?.link || ''}
|
||||
h={360}
|
||||
radius="xl"
|
||||
style={{ overflow: 'hidden', position: 'relative' }}
|
||||
onMouseEnter={() => setHoveredId(v.id)}
|
||||
onMouseLeave={() => setHoveredId(null)}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
cursor: 'pointer',
|
||||
transition: 'transform 0.3s ease'
|
||||
}}
|
||||
>
|
||||
{/* Overlay with smooth transition */}
|
||||
<Box
|
||||
pos="absolute"
|
||||
inset={0}
|
||||
bg="linear-gradient(180deg, rgba(0,0,0,0.25) 0%, rgba(0,0,0,0.7) 100%)"
|
||||
bg={hoveredId === v.id
|
||||
? "linear-gradient(180deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.75) 100%)"
|
||||
: "linear-gradient(180deg, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.15) 100%)"
|
||||
}
|
||||
style={{
|
||||
transition: 'background 0.3s ease'
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack justify="space-between" h="100%" gap="md" p="lg" pos="relative">
|
||||
{/* Kategori badge - always visible */}
|
||||
<Group>
|
||||
<Paper radius="lg" py={6} px={12} shadow="md" withBorder bg="rgba(255,255,255,0.85)">
|
||||
<Paper
|
||||
radius="lg"
|
||||
py={6}
|
||||
px={12}
|
||||
shadow="md"
|
||||
withBorder
|
||||
bg="rgba(255,255,255,0.9)"
|
||||
style={{
|
||||
transition: 'all 0.3s ease'
|
||||
}}
|
||||
>
|
||||
<Text fz="sm" fw={600}>{v.kategori?.nama}</Text>
|
||||
</Paper>
|
||||
</Group>
|
||||
<Box>
|
||||
|
||||
{/* Nama potensi - visible on hover */}
|
||||
<Box
|
||||
style={{
|
||||
opacity: hoveredId === v.id ? 1 : 0,
|
||||
transform: hoveredId === v.id ? 'translateY(0)' : 'translateY(10px)',
|
||||
transition: 'all 0.3s ease',
|
||||
pointerEvents: hoveredId === v.id ? 'auto' : 'none'
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
fw={800}
|
||||
c="white"
|
||||
@@ -113,18 +149,28 @@ function Page() {
|
||||
{v.name}
|
||||
</Text>
|
||||
</Box>
|
||||
<Group justify="center">
|
||||
<Button
|
||||
radius="xl"
|
||||
size="md"
|
||||
leftSection={<IconEye size={18} />}
|
||||
bg={colors["blue-button"]}
|
||||
variant="gradient"
|
||||
gradient={{ from: colors["blue-button"], to: "#4dabf7", deg: 45 }}
|
||||
onClick={() => router.push(`/darmasaba/desa/potensi/${v.id}`)}
|
||||
>
|
||||
Lihat Detail
|
||||
</Button>
|
||||
|
||||
{/* Button - visible on hover */}
|
||||
<Group
|
||||
justify="center"
|
||||
style={{
|
||||
opacity: hoveredId === v.id ? 1 : 0,
|
||||
transform: hoveredId === v.id ? 'translateY(0)' : 'translateY(10px)',
|
||||
transition: 'all 0.3s ease',
|
||||
pointerEvents: hoveredId === v.id ? 'auto' : 'none'
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
radius="xl"
|
||||
size="md"
|
||||
leftSection={<IconEye size={18} />}
|
||||
bg={colors["blue-button"]}
|
||||
variant="gradient"
|
||||
gradient={{ from: colors["blue-button"], to: "#4dabf7", deg: 45 }}
|
||||
onClick={() => router.push(`/darmasaba/desa/potensi/${v.id}`)}
|
||||
>
|
||||
Lihat Detail
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</BackgroundImage>
|
||||
@@ -147,4 +193,4 @@ function Page() {
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
export default Page;
|
||||
@@ -0,0 +1,83 @@
|
||||
'use client'
|
||||
import desaDigitalState from '@/app/admin/(dashboard)/_state/inovasi/desa-digital';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Center, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import BackButton from '../../../desa/layanan/_com/BackButto';
|
||||
|
||||
function DetailDesaDigitalUser() {
|
||||
const stateDesaDigital = useProxy(desaDigitalState);
|
||||
const params = useParams();
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateDesaDigital.findUnique.load(params?.id as string);
|
||||
}, []);
|
||||
|
||||
if (!stateDesaDigital.findUnique.data) {
|
||||
return (
|
||||
<Stack py={40} align="center">
|
||||
<Skeleton height={400} radius="md" w={{ base: "90%", md: "60%" }} />
|
||||
<Text fz="lg" c="dimmed">Memuat data...</Text>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const data = stateDesaDigital.findUnique.data;
|
||||
|
||||
return (
|
||||
<Stack bg={colors.Bg} py="xl" align="center" px={{ base: "md", md: "lg" }}>
|
||||
{/* Tombol Back */}
|
||||
<Box w={{ base: "100%", md: "60%" }}>
|
||||
<BackButton/>
|
||||
</Box>
|
||||
|
||||
{/* Card Detail */}
|
||||
<Paper
|
||||
w={{ base: "100%", md: "60%" }}
|
||||
bg={colors["white-1"]}
|
||||
p="lg"
|
||||
radius="md"
|
||||
shadow="sm"
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Text ta={"center"} fz={{ base: "xl", md: "2xl" }} fw="bold" c={colors["blue-button"]}>
|
||||
{data?.name || "Desa Digital"}
|
||||
</Text>
|
||||
|
||||
{/* Gambar */}
|
||||
{data?.image?.link ? (
|
||||
<Center>
|
||||
<Image
|
||||
src={data.image.link}
|
||||
alt={data.name || "Gambar Desa Digital"}
|
||||
radius="md"
|
||||
fit="cover"
|
||||
w={{ base: "100%", md: "80%" }}
|
||||
h={{ base: 250, md: 350 }}
|
||||
style={{ objectPosition: "center", borderRadius: 12 }}
|
||||
/>
|
||||
</Center>
|
||||
) : (
|
||||
<Center>
|
||||
<Text c="dimmed">Tidak ada gambar</Text>
|
||||
</Center>
|
||||
)}
|
||||
|
||||
{/* Deskripsi */}
|
||||
<Box pt="md">
|
||||
<Text
|
||||
fz={{ base: "md", md: "lg" }}
|
||||
c="dimmed"
|
||||
style={{ lineHeight: 1.6 }}
|
||||
dangerouslySetInnerHTML={{ __html: data?.deskripsi || "-" }}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailDesaDigitalUser;
|
||||
@@ -1,17 +1,19 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Box, Text, Paper, SimpleGrid, Image, Skeleton, Center, Pagination, Grid, GridCol, TextInput } from '@mantine/core';
|
||||
import { Stack, Box, Text, Paper, SimpleGrid, Image, Skeleton, Center, Pagination, Grid, GridCol, TextInput, Button } from '@mantine/core';
|
||||
import React, { useState } from 'react';
|
||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import desaDigitalState from '@/app/admin/(dashboard)/_state/inovasi/desa-digital';
|
||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
function Page() {
|
||||
const [search, setSearch] = useState("")
|
||||
const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
|
||||
const state = useProxy(desaDigitalState)
|
||||
const router = useRouter()
|
||||
const {
|
||||
data,
|
||||
page,
|
||||
@@ -39,10 +41,10 @@ function Page() {
|
||||
<BackButton />
|
||||
</Box>
|
||||
<Box px={{ base: 'md', md: 100 }} >
|
||||
<Grid align='center'>
|
||||
<Grid align='center'>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||
Desa Digital / Smart Village
|
||||
Desa Digital / Smart Village
|
||||
</Text>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
@@ -70,12 +72,62 @@ function Page() {
|
||||
>
|
||||
{filteredData.map((v, k) => {
|
||||
return (
|
||||
<Paper p={'xl'} key={k}>
|
||||
<Image src={v.image.link? v.image.link : ''} pb={10} radius={10} alt='' loading="lazy"/>
|
||||
<Text fz={'h3'} fw={'bold'} c={colors['blue-button']}>{v.name}</Text>
|
||||
<Box>
|
||||
<Text fz={"md"} style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
|
||||
</Box>
|
||||
<Paper
|
||||
key={k}
|
||||
radius="xl"
|
||||
shadow="md"
|
||||
withBorder
|
||||
p="lg"
|
||||
bg={colors['white-trans-1']}
|
||||
style={{
|
||||
transition: 'all 200ms ease',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between', // ✅ biar button selalu di bawah
|
||||
height: '100%', // ✅ bikin tinggi seragam
|
||||
}}
|
||||
>
|
||||
<Stack gap={"xs"}>
|
||||
<Box
|
||||
style={{
|
||||
width: '100%',
|
||||
aspectRatio: '16/9',
|
||||
borderRadius: '12px',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={v.image.link}
|
||||
alt={v.name}
|
||||
fit="cover"
|
||||
loading="lazy"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
transition: 'transform 0.4s ease',
|
||||
}}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.transform = 'scale(1.05)')}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.transform = 'scale(1)')}
|
||||
/>
|
||||
</Box>
|
||||
<Text fz={'h3'} fw={'bold'} c={colors['blue-button']}>{v.name}</Text>
|
||||
<Box>
|
||||
<Text lineClamp={3} truncate="end" fz={"md"} style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Center mt="md">
|
||||
<Button
|
||||
bg={colors['blue-button']}
|
||||
radius="lg"
|
||||
w="100%"
|
||||
onClick={() => router.push(`/darmasaba/inovasi/desa-digital-smart-village/${v.id}`)}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</Center>
|
||||
</Paper>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text, Skeleton } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import programKreatifState from '@/app/admin/(dashboard)/_state/inovasi/program-kreatif';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
import { IconMapper, IconKey } from '@/app/admin/(dashboard)/_com/iconMap';
|
||||
import { IconKey, IconMapper } from '@/app/admin/(dashboard)/_com/iconMap';
|
||||
import BackButton from '../../../desa/layanan/_com/BackButto';
|
||||
|
||||
function Page() {
|
||||
const stateProgramKreatif = useProxy(programKreatifState);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
useShallowEffect(() => {
|
||||
@@ -31,14 +31,7 @@ function Page() {
|
||||
<Box px={{ base: 'md', md: 100 }} py="md">
|
||||
{/* Tombol Kembali */}
|
||||
<Box mb="md">
|
||||
<Text
|
||||
c={colors['blue-button']}
|
||||
fw="bold"
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
← Kembali
|
||||
</Text>
|
||||
<BackButton/>
|
||||
</Box>
|
||||
|
||||
{/* Konten Utama */}
|
||||
|
||||
@@ -117,7 +117,7 @@ function Page() {
|
||||
)}
|
||||
</Center>
|
||||
<Text ta={'center'} fz={'h3'} fw={'bold'} c={colors['blue-button']}>{v.name}</Text>
|
||||
<Text py={10} ta={'center'} fz={'lg'} c={'black'}>{v.slug}</Text>
|
||||
<Text lineClamp={2} lh={"1.9"} py={10} ta={'center'} fz={'lg'} c={'black'}>{v.slug}</Text>
|
||||
<Center>
|
||||
<Button onClick={() => router.push(`/darmasaba/inovasi/program-kreatif-desa/${v.id}`)} bg={colors['blue-button']}>Selengkapnya</Button>
|
||||
</Center>
|
||||
|
||||
@@ -1,80 +1,132 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
'use client';
|
||||
|
||||
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
|
||||
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Center, Container, Image, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { Box, Image, Paper, Skeleton, Stack, Text, Title, Group } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconCalendar, IconMapPin, IconUsers } from '@tabler/icons-react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import colors from '@/con/colors';
|
||||
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
|
||||
|
||||
function DetailKegiatanDesaUser() {
|
||||
const kegiatanDesaState = useProxy(gotongRoyongState.kegiatanDesa);
|
||||
const params = useParams();
|
||||
|
||||
useShallowEffect(() => {
|
||||
kegiatanDesaState.findUnique.load(params?.id as string);
|
||||
}, []);
|
||||
|
||||
const data = kegiatanDesaState.findUnique.data;
|
||||
|
||||
function Page() {
|
||||
const params = useParams<{ id: string }>();
|
||||
const id = Array.isArray(params.id) ? params.id[0] : params.id;
|
||||
const state = useProxy(gotongRoyongState.kegiatanDesa)
|
||||
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) {
|
||||
if (!data) {
|
||||
return (
|
||||
<Center>
|
||||
<Skeleton height={500} />
|
||||
</Center>
|
||||
<Stack py={20}>
|
||||
<Skeleton height={400} radius="md" />
|
||||
<Skeleton height={20} width="70%" />
|
||||
<Skeleton height={15} width="50%" />
|
||||
<Skeleton height={300} radius="md" />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
if (!state.findUnique.data) {
|
||||
return (
|
||||
<Center>
|
||||
<Text>Data tidak ditemukan</Text>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"} px={{ base: "md", md: 0 }}>
|
||||
<Box px={{ base: "md", md: 100 }}><BackButton /></Box>
|
||||
<Container w={{ base: "100%", md: "50%" }} >
|
||||
<Box pb={20}>
|
||||
<Text ta={"center"} fz={"2.4rem"} c={colors["blue-button"]} fw={"bold"}>
|
||||
{state.findUnique.data?.judul}
|
||||
</Text>
|
||||
<Text
|
||||
ta={"center"}
|
||||
fw={"bold"}
|
||||
fz={"1.5rem"}
|
||||
>
|
||||
Informasi Kegiatan Gotong Royong
|
||||
</Text>
|
||||
</Box>
|
||||
<Image src={state.findUnique.data?.image?.link || ''} alt='' w={"100%"} loading="lazy"/>
|
||||
</Container>
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Text py={20} style={{wordBreak: "break-word", whiteSpace: "normal"}} fz={{ base: "sm", md: "lg" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: state.findUnique.data?.deskripsiLengkap || '' }} />
|
||||
<Box py={30}>
|
||||
{/* Header Gambar */}
|
||||
|
||||
{/* Konten */}
|
||||
<Paper
|
||||
bg={colors['white-1']}
|
||||
p="lg"
|
||||
radius="md"
|
||||
shadow="sm"
|
||||
maw={900}
|
||||
mx="auto"
|
||||
>
|
||||
<Stack gap="md">
|
||||
{data.image?.link && (
|
||||
<Image
|
||||
src={data.image.link}
|
||||
alt={data.judul || 'Kegiatan Desa'}
|
||||
radius="md"
|
||||
fit="cover"
|
||||
h={300}
|
||||
mb="xl"
|
||||
style={{ objectPosition: 'center', width: '100%' }}
|
||||
/>
|
||||
)}
|
||||
{/* Judul */}
|
||||
<Title order={2} c={colors['blue-button']}>
|
||||
{data.judul || 'Kegiatan Desa'}
|
||||
</Title>
|
||||
|
||||
{/* Meta Info */}
|
||||
<Group gap="xl" c="dimmed">
|
||||
<Group gap={6}>
|
||||
<IconCalendar size={18} />
|
||||
<Text fz="sm">
|
||||
{data.tanggal ? new Date(data.tanggal).toLocaleDateString('id-ID', { day: 'numeric', month: 'long', year: 'numeric' }) : '-'}
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
{data.lokasi && (
|
||||
<Group gap={6}>
|
||||
<IconMapPin size={18} />
|
||||
<Text fz="sm">{data.lokasi}</Text>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
{data.partisipan && (
|
||||
<Group gap={6}>
|
||||
<IconUsers size={18} />
|
||||
<Text fz="sm">{data.partisipan}</Text>
|
||||
</Group>
|
||||
)}
|
||||
</Group>
|
||||
|
||||
{/* Deskripsi Singkat */}
|
||||
{data.deskripsiSingkat && (
|
||||
<Box>
|
||||
<Text fw={600} fz="lg" mb={4}>
|
||||
Ringkasan
|
||||
</Text>
|
||||
<Text
|
||||
fz="md"
|
||||
c="dimmed"
|
||||
dangerouslySetInnerHTML={{ __html: data.deskripsiSingkat }}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Deskripsi Lengkap */}
|
||||
{data.deskripsiLengkap && (
|
||||
<Box>
|
||||
<Text fw={600} fz="lg" mb={4}>
|
||||
Detail Kegiatan
|
||||
</Text>
|
||||
<Text
|
||||
fz="md"
|
||||
c="dimmed"
|
||||
style={{ lineHeight: 1.6 }}
|
||||
dangerouslySetInnerHTML={{ __html: data.deskripsiLengkap }}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Kategori */}
|
||||
{data.kategoriKegiatan?.nama && (
|
||||
<Box mt="md">
|
||||
<Text fw={600} fz="lg">
|
||||
Kategori
|
||||
</Text>
|
||||
<Text fz="md" c="dimmed">
|
||||
{data.kategoriKegiatan.nama}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
export default DetailKegiatanDesaUser;
|
||||
|
||||
Reference in New Issue
Block a user