Compare commits
6 Commits
nico/22-ok
...
nico/29-ok
| Author | SHA1 | Date | |
|---|---|---|---|
| 0befe6a3f2 | |||
| a6663bbcee | |||
| ed371bd0d9 | |||
| f82c7b86e0 | |||
| b5d6585cd5 | |||
| aa98359ef7 |
@@ -3,7 +3,7 @@
|
|||||||
"version": "0.1.5",
|
"version": "0.1.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun --bun next dev --hostname 0.0.0.0",
|
"dev": "bun --bun next dev",
|
||||||
"build": "bun --bun next build",
|
"build": "bun --bun next build",
|
||||||
"start": "bun --bun next start"
|
"start": "bun --bun next start"
|
||||||
},
|
},
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
"@types/bun": "^1.2.2",
|
"@types/bun": "^1.2.2",
|
||||||
"@types/leaflet": "^1.9.20",
|
"@types/leaflet": "^1.9.20",
|
||||||
"@types/lodash": "^4.17.16",
|
"@types/lodash": "^4.17.16",
|
||||||
|
"@types/nodemailer": "^7.0.2",
|
||||||
"add": "^2.0.6",
|
"add": "^2.0.6",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
"elysia": "^1.3.5",
|
"elysia": "^1.3.5",
|
||||||
"embla-carousel-autoplay": "^8.5.2",
|
"embla-carousel-autoplay": "^8.5.2",
|
||||||
"embla-carousel-react": "^7.1.0",
|
"embla-carousel-react": "^7.1.0",
|
||||||
@@ -71,6 +73,7 @@
|
|||||||
"next": "^15.5.2",
|
"next": "^15.5.2",
|
||||||
"next-view-transitions": "^0.3.4",
|
"next-view-transitions": "^0.3.4",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
|
"nodemailer": "^7.0.10",
|
||||||
"p-limit": "^6.2.0",
|
"p-limit": "^6.2.0",
|
||||||
"primeicons": "^7.0.0",
|
"primeicons": "^7.0.0",
|
||||||
"primereact": "^10.9.6",
|
"primereact": "^10.9.6",
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ function EditArtikelKesehatan() {
|
|||||||
{/* Gambar */}
|
{/* Gambar */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" fz="sm" mb={6}>
|
<Text fw="bold" fz="sm" mb={6}>
|
||||||
Gambar Berita
|
Gambar Artikel Kesehatan
|
||||||
</Text>
|
</Text>
|
||||||
<Dropzone
|
<Dropzone
|
||||||
onDrop={handleFileChange}
|
onDrop={handleFileChange}
|
||||||
@@ -240,15 +240,15 @@ function EditArtikelKesehatan() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Pendahuluan */}
|
{/* Pendahuluan */}
|
||||||
<InputText
|
<Box>
|
||||||
label="Pendahuluan"
|
<Text fw="bold">Pendahuluan</Text>
|
||||||
|
<EditEditor
|
||||||
value={formData.introduction.content}
|
value={formData.introduction.content}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
setFormData((prev) => ({ ...prev, introduction: { content: value } }))
|
setFormData((prev) => ({ ...prev, introduction: { ...prev.introduction, content: value } }))
|
||||||
}
|
}
|
||||||
placeholder="Masukkan pendahuluan"
|
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
{/* Gejala */}
|
{/* Gejala */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold">Gejala</Text>
|
<Text fw="bold">Gejala</Text>
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ function CreateArtikelKesehatan() {
|
|||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" fz="sm" mb={6}>
|
<Text fw="bold" fz="sm" mb={6}>
|
||||||
Gambar Berita
|
Gambar Artikel Kesehatan
|
||||||
</Text>
|
</Text>
|
||||||
<Dropzone
|
<Dropzone
|
||||||
onDrop={(files) => {
|
onDrop={(files) => {
|
||||||
@@ -182,16 +182,15 @@ function CreateArtikelKesehatan() {
|
|||||||
}}
|
}}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<Box>
|
||||||
label={"Pendahuluan"}
|
<Text fz="sm" fw="bold">Pendahuluan</Text>
|
||||||
placeholder="Masukkan pendahuluan"
|
<CreateEditor
|
||||||
required
|
value={stateArtikelKesehatan.create.form.introduction.content}
|
||||||
defaultValue={stateArtikelKesehatan.create.form.introduction.content}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
stateArtikelKesehatan.create.form.introduction.content = e.target.value;
|
stateArtikelKesehatan.create.form.introduction.content = e;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
{/* Gejala */}
|
{/* Gejala */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="md" fw="bold">Gejala</Text>
|
<Text fz="md" fw="bold">Gejala</Text>
|
||||||
|
|||||||
54
src/app/api/subscribe/route.ts
Normal file
54
src/app/api/subscribe/route.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import nodemailer from 'nodemailer';
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
try {
|
||||||
|
const { email } = await request.json();
|
||||||
|
|
||||||
|
// Input validation
|
||||||
|
if (!email) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: 'Email is required' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email regex validation
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
if (!emailRegex.test(email)) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: 'Invalid email format' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure nodemailer
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
service: 'gmail',
|
||||||
|
auth: {
|
||||||
|
user: process.env.EMAIL_USER,
|
||||||
|
pass: process.env.EMAIL_PASS,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send email
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: `"Tim Info" <${process.env.EMAIL_USER}>`,
|
||||||
|
to: email,
|
||||||
|
subject: '✅ Berhasil Berlangganan!',
|
||||||
|
html: `<p>Terima kasih telah berlangganan info terbaru dari kami!</p>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Subscription successful! Please check your email.',
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in subscribe API:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: 'Internal server error' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,25 +7,39 @@ import { useEffect, useState } from 'react';
|
|||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
function PelayananPerizinanBerusaha() {
|
function PelayananPerizinanBerusaha() {
|
||||||
const state = useProxy(stateLayananDesa)
|
const state = useProxy(stateLayananDesa);
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false);
|
||||||
const [active, setActive] = useState(1);
|
const [active, setActive] = useState(0);
|
||||||
const nextStep = () => setActive((current) => (current < 6 ? current + 1 : current));
|
|
||||||
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
|
const totalSteps = 6;
|
||||||
|
|
||||||
|
const nextStep = () => {
|
||||||
|
if (active < totalSteps - 1) {
|
||||||
|
setActive(active + 1);
|
||||||
|
} else if (active === totalSteps - 1) {
|
||||||
|
setActive(totalSteps); // Mark as completed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const prevStep = () => {
|
||||||
|
if (active > 0) {
|
||||||
|
setActive(active - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
await state.pelayananPerizinanBerusaha.findById.load('edit')
|
await state.pelayananPerizinanBerusaha.findById.load('edit');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Gagal memuat data:', error);
|
console.error('Gagal memuat data:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
loadData()
|
loadData();
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const data = state.pelayananPerizinanBerusaha.findById.data;
|
const data = state.pelayananPerizinanBerusaha.findById.data;
|
||||||
|
|
||||||
@@ -33,8 +47,12 @@ function PelayananPerizinanBerusaha() {
|
|||||||
return (
|
return (
|
||||||
<Center mih={300}>
|
<Center mih={300}>
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<Text fz="lg" fw={500} c="dimmed">Belum ada informasi layanan yang tersedia</Text>
|
<Text fz="lg" fw={500} c="dimmed">
|
||||||
<Button component="a" href="https://oss.go.id" target="_blank" radius="xl">Kunjungi OSS</Button>
|
Belum ada informasi layanan yang tersedia
|
||||||
|
</Text>
|
||||||
|
<Button component="a" href="https://oss.go.id" target="_blank" radius="xl">
|
||||||
|
Kunjungi OSS
|
||||||
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
);
|
);
|
||||||
@@ -57,11 +75,27 @@ function PelayananPerizinanBerusaha() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Text fz={{ base: 'sm', md: 'md' }} ta="justify" style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: data?.deskripsi || '' }} />
|
<Text
|
||||||
|
fz={{ base: 'sm', md: 'md' }}
|
||||||
|
ta="justify"
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: data?.deskripsi || '' }}
|
||||||
|
/>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw={600} mb="sm" fz={{ base: 'sm', md: 'lg' }}>Alur pendaftaran NIB:</Text>
|
<Text fw={600} mb="sm" fz={{ base: 'sm', md: 'lg' }}>
|
||||||
<Stepper active={active} onStepClick={setActive} orientation="vertical" color="blue" radius="md"
|
Alur pendaftaran NIB:
|
||||||
|
</Text>
|
||||||
|
<Stepper
|
||||||
|
active={active}
|
||||||
|
onStepClick={(step) => {
|
||||||
|
if (step <= active) { // Only allow clicking on previous or current steps
|
||||||
|
setActive(step);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
orientation="vertical"
|
||||||
|
color="blue"
|
||||||
|
radius="md"
|
||||||
styles={{
|
styles={{
|
||||||
step: { padding: '14px 0' },
|
step: { padding: '14px 0' },
|
||||||
stepBody: { marginLeft: 8 }
|
stepBody: { marginLeft: 8 }
|
||||||
@@ -95,19 +129,42 @@ function PelayananPerizinanBerusaha() {
|
|||||||
</StepperCompleted>
|
</StepperCompleted>
|
||||||
</Stepper>
|
</Stepper>
|
||||||
|
|
||||||
|
{active < totalSteps && (
|
||||||
<Group justify="center" mt="lg">
|
<Group justify="center" mt="lg">
|
||||||
<Button variant="light" leftSection={<IconArrowLeft size={18} />} onClick={prevStep} disabled={active === 0}>
|
<Button
|
||||||
|
variant="light"
|
||||||
|
leftSection={<IconArrowLeft size={18} />}
|
||||||
|
onClick={prevStep}
|
||||||
|
disabled={active === 0}
|
||||||
|
>
|
||||||
Kembali
|
Kembali
|
||||||
</Button>
|
</Button>
|
||||||
<Button rightSection={<IconArrowRight size={18} />} onClick={nextStep}>
|
|
||||||
Lanjut
|
{active < totalSteps ? (
|
||||||
|
<Button
|
||||||
|
rightSection={active < totalSteps - 1 ? <IconArrowRight size={18} /> : null}
|
||||||
|
onClick={nextStep}
|
||||||
|
>
|
||||||
|
{active === totalSteps - 1 ? 'Selesai' : 'Lanjut'}
|
||||||
</Button>
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
onClick={() => setActive(0)}
|
||||||
|
>
|
||||||
|
Mulai Lagi
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Text fz="sm" ta="justify" c="dimmed" mt="md">
|
<Text fz="sm" ta="justify" c="dimmed" mt="md">
|
||||||
Catatan: Persyaratan dan prosedur dapat berubah sewaktu-waktu. Untuk informasi resmi terbaru, silakan kunjungi situs{" "}
|
Catatan: Persyaratan dan prosedur dapat berubah sewaktu-waktu. Untuk informasi resmi terbaru, silakan kunjungi situs{' '}
|
||||||
<a href="https://oss.go.id/" target="_blank" rel="noopener noreferrer">oss.go.id</a> atau hubungi instansi pemerintah terkait.
|
<a href="https://oss.go.id/" target="_blank" rel="noopener noreferrer">
|
||||||
|
oss.go.id
|
||||||
|
</a>{' '}
|
||||||
|
atau hubungi instansi pemerintah terkait.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ function PelayananSuratKeterangan({ search }: { search: string }) {
|
|||||||
<Box pb="xl">
|
<Box pb="xl">
|
||||||
<Group justify="space-between" align="center" mb="md">
|
<Group justify="space-between" align="center" mb="md">
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<IconFileDescription size={28} stroke={1.8} color={colors["blue-button"]} />
|
<IconFileDescription size={28} stroke={1.8} />
|
||||||
<Text fz={{ base: "h4", md: "h2" }} fw={700}>
|
<Text fz={{ base: "h4", md: "h2" }} fw={700}>
|
||||||
Layanan Surat Keterangan
|
Layanan Surat Keterangan
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Tooltip label="Pilih layanan surat keterangan sesuai kebutuhan Anda" withArrow>
|
<Tooltip label="Pilih layanan surat keterangan sesuai kebutuhan Anda" withArrow>
|
||||||
<IconInfoCircle size={22} stroke={1.8} color={colors["blue-button"]} />
|
<IconInfoCircle size={22} stroke={1.8} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
|||||||
@@ -67,16 +67,26 @@ function Page() {
|
|||||||
<Text ta="center" fw={600} fz={{ base: "md", md: "lg" }} c="dimmed">
|
<Text ta="center" fw={600} fz={{ base: "md", md: "lg" }} c="dimmed">
|
||||||
Informasi & Pelayanan Potensi Desa Digital
|
Informasi & Pelayanan Potensi Desa Digital
|
||||||
</Text>
|
</Text>
|
||||||
|
{/* ✅ Bagian gambar dibuat konsisten tanpa CSS manual */}
|
||||||
|
<Box
|
||||||
|
w="100%"
|
||||||
|
h={{ base: 220, md: 400 }}
|
||||||
|
style={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
borderRadius: 'var(--mantine-radius-lg)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
src={state.findUnique.data?.image?.link || ''}
|
src={state.findUnique.data?.image?.link || ''}
|
||||||
alt={state.findUnique.data?.name || 'Potensi Desa'}
|
alt={state.findUnique.data?.name || 'Potensi Desa'}
|
||||||
radius="lg"
|
|
||||||
fit="cover"
|
fit="cover"
|
||||||
w="100%"
|
w="100%"
|
||||||
h={{ base: 220, md: 400 }}
|
h="100%"
|
||||||
fallbackSrc="https://placehold.co/800x400?text=Gambar+tidak+tersedia"
|
fallbackSrc="https://placehold.co/800x400?text=Gambar+tidak+tersedia"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
|
radius="lg"
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
<Text py="md" fz={{ base: "sm", md: "md" }} ta="justify" lh={1.8} dangerouslySetInnerHTML={{ __html: state.findUnique.data?.deskripsi || 'Belum ada deskripsi untuk potensi desa ini.' }} />
|
<Text py="md" fz={{ base: "sm", md: "md" }} ta="justify" lh={1.8} dangerouslySetInnerHTML={{ __html: state.findUnique.data?.deskripsi || 'Belum ada deskripsi untuk potensi desa ini.' }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import potensiDesaState from '@/app/admin/(dashboard)/_state/desa/potensi';
|
import potensiDesaState from '@/app/admin/(dashboard)/_state/desa/potensi';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { BackgroundImage, Box, Button, Center, Flex, Group, Paper, SimpleGrid, Skeleton, Stack, Text, Tooltip } from '@mantine/core';
|
import { BackgroundImage, Box, Button, Center, Flex, Group, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
import { IconEye } from '@tabler/icons-react';
|
import { IconEye } from '@tabler/icons-react';
|
||||||
import { useTransitionRouter } from 'next-view-transitions';
|
import { useTransitionRouter } from 'next-view-transitions';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@@ -43,7 +43,7 @@ function Page() {
|
|||||||
<Text fz={{ base: "2rem", md: "3rem" }} fw={900} c={colors["blue-button"]} lh={1.2}>
|
<Text fz={{ base: "2rem", md: "3rem" }} fw={900} c={colors["blue-button"]} lh={1.2}>
|
||||||
Potensi Desa Darmasaba
|
Potensi Desa Darmasaba
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="lg" c="dimmed" ta="justify">
|
<Text fz="lg" ta="justify">
|
||||||
Temukan berbagai potensi unggulan, peluang, dan daya tarik yang menjadikan Desa Darmasaba istimewa.
|
Temukan berbagai potensi unggulan, peluang, dan daya tarik yang menjadikan Desa Darmasaba istimewa.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -114,7 +114,6 @@ function Page() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Group justify="center">
|
<Group justify="center">
|
||||||
<Tooltip label="Lihat detail potensi" withArrow>
|
|
||||||
<Button
|
<Button
|
||||||
radius="xl"
|
radius="xl"
|
||||||
size="md"
|
size="md"
|
||||||
@@ -126,7 +125,6 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Lihat Detail
|
Lihat Detail
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</BackgroundImage>
|
</BackgroundImage>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile'
|
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile'
|
||||||
import colors from '@/con/colors'
|
import colors from '@/con/colors'
|
||||||
import { Box, Center, Image, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core'
|
import { Box, Center, Image, Paper, Skeleton, Stack, Text } from '@mantine/core'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useProxy } from 'valtio/utils'
|
import { useProxy } from 'valtio/utils'
|
||||||
|
|
||||||
@@ -58,16 +58,14 @@ function LambangDesa() {
|
|||||||
borderColor: '#e0e9ff',
|
borderColor: '#e0e9ff',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tooltip label="Deskripsi lambang desa" position="top-start" withArrow>
|
|
||||||
<Text
|
<Text
|
||||||
fz={{ base: 'md', md: 'lg' }}
|
fz={{ base: '1.125rem', md: '1.375rem' }}
|
||||||
lh={1.8}
|
lh={1.8}
|
||||||
c="dark"
|
c="dark"
|
||||||
ta="justify"
|
ta="justify"
|
||||||
style={{ fontWeight: 400, wordBreak: "break-word", whiteSpace: "normal", }}
|
style={{ fontWeight: 400, wordBreak: "break-word", whiteSpace: "normal", }}
|
||||||
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
||||||
import { Box, Card, Center, Group, Image, Loader, Paper, Stack, Text, Tooltip } from '@mantine/core';
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Card, Center, Group, Image, Loader, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconPhoto } from '@tabler/icons-react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import { IconPhoto } from '@tabler/icons-react';
|
|
||||||
import colors from '@/con/colors';
|
|
||||||
|
|
||||||
function MaskotDesa() {
|
function MaskotDesa() {
|
||||||
const state = useProxy(stateProfileDesa.maskotDesa);
|
const state = useProxy(stateProfileDesa.maskotDesa);
|
||||||
@@ -54,8 +54,8 @@ function MaskotDesa() {
|
|||||||
<Group justify="center" gap="lg" mt="lg">
|
<Group justify="center" gap="lg" mt="lg">
|
||||||
{data.images.length > 0 ? (
|
{data.images.length > 0 ? (
|
||||||
data.images.map((img, index) => (
|
data.images.map((img, index) => (
|
||||||
<Tooltip key={index} label={img.label} position="bottom" withArrow>
|
|
||||||
<Card
|
<Card
|
||||||
|
key={index}
|
||||||
radius="lg"
|
radius="lg"
|
||||||
shadow="md"
|
shadow="md"
|
||||||
withBorder
|
withBorder
|
||||||
@@ -79,7 +79,6 @@ function MaskotDesa() {
|
|||||||
{img.label}
|
{img.label}
|
||||||
</Text>
|
</Text>
|
||||||
</Card>
|
</Card>
|
||||||
</Tooltip>
|
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<Stack align="center" gap="xs" mt="lg">
|
<Stack align="center" gap="xs" mt="lg">
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Image, Paper, SimpleGrid, Skeleton, Stack, Text, Divider, Tooltip } from '@mantine/core';
|
import { Box, Divider, Image, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconBriefcase, IconTargetArrow, IconUser, IconUsers } from '@tabler/icons-react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import { IconUser, IconBriefcase, IconUsers, IconTargetArrow } from '@tabler/icons-react';
|
|
||||||
|
|
||||||
function ProfilPerbekel() {
|
function ProfilPerbekel() {
|
||||||
const state = useProxy(stateProfileDesa.profilPerbekel)
|
const state = useProxy(stateProfileDesa.profilPerbekel)
|
||||||
@@ -93,10 +93,9 @@ function ProfilPerbekel() {
|
|||||||
>
|
>
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
<Box>
|
<Box>
|
||||||
<Tooltip label="Informasi pribadi perbekel" withArrow>
|
|
||||||
<Stack gap={6}>
|
<Stack gap={6}>
|
||||||
<Stack align="center" gap={6}>
|
<Stack align="center" gap={6}>
|
||||||
<IconUser size={22} color={colors['blue-button']} />
|
<IconUser size={22} />
|
||||||
<Text fz={{ base: "1.2rem", md: "1.5rem" }} fw="bold">Biodata</Text>
|
<Text fz={{ base: "1.2rem", md: "1.5rem" }} fw="bold">Biodata</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Text
|
<Text
|
||||||
@@ -107,14 +106,12 @@ function ProfilPerbekel() {
|
|||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Tooltip label="Pengalaman kerja perbekel" withArrow>
|
|
||||||
<Stack gap={6}>
|
<Stack gap={6}>
|
||||||
<Stack align="center" gap={6}>
|
<Stack align="center" gap={6}>
|
||||||
<IconBriefcase size={22} color={colors['blue-button']} />
|
<IconBriefcase size={22} />
|
||||||
<Text fz={{ base: "1.2rem", md: "1.5rem" }} fw="bold">Pengalaman</Text>
|
<Text fz={{ base: "1.2rem", md: "1.5rem" }} fw="bold">Pengalaman</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Text
|
<Text
|
||||||
@@ -125,7 +122,6 @@ function ProfilPerbekel() {
|
|||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
@@ -142,7 +138,7 @@ function ProfilPerbekel() {
|
|||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
<Box>
|
<Box>
|
||||||
<Stack align="center" gap={6} >
|
<Stack align="center" gap={6} >
|
||||||
<IconUsers size={22} color={colors['blue-button']} />
|
<IconUsers size={22} />
|
||||||
<Text fz={{ base: "1.2rem", md: "1.5rem" }} fw="bold">Pengalaman Organisasi</Text>
|
<Text fz={{ base: "1.2rem", md: "1.5rem" }} fw="bold">Pengalaman Organisasi</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Text
|
<Text
|
||||||
@@ -156,7 +152,7 @@ function ProfilPerbekel() {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Stack align="center" gap={6} mb={6}>
|
<Stack align="center" gap={6} mb={6}>
|
||||||
<IconTargetArrow size={22} color={colors['blue-button']} />
|
<IconTargetArrow size={22} />
|
||||||
<Text fz={{ base: "1.2rem", md: "1.5rem" }} fw="bold">Program Kerja Unggulan</Text>
|
<Text fz={{ base: "1.2rem", md: "1.5rem" }} fw="bold">Program Kerja Unggulan</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Box px={10}>
|
<Box px={10}>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
||||||
import { Box, Center, Image, Paper, SimpleGrid, Skeleton, Stack, Text, Title, Tooltip } from '@mantine/core';
|
import { Box, Center, Image, Paper, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconUser } from '@tabler/icons-react';
|
import { IconUser } from '@tabler/icons-react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
@@ -77,23 +77,17 @@ function SemuaPerbekel() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Stack gap={4} align="center">
|
<Stack gap={4} align="center">
|
||||||
<Tooltip label="Nama Perbekel" withArrow>
|
|
||||||
<Text fw={700} fz="lg" ta="center">
|
<Text fw={700} fz="lg" ta="center">
|
||||||
{v.nama}
|
{v.nama}
|
||||||
</Text>
|
</Text>
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip label="Wilayah menjabat" withArrow>
|
|
||||||
<Text c="dimmed" fz="sm" ta="center">
|
<Text c="dimmed" fz="sm" ta="center">
|
||||||
{v.daerah}
|
{v.daerah}
|
||||||
</Text>
|
</Text>
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip label="Periode jabatan" withArrow>
|
|
||||||
<Text c="blue" fw={600} fz="sm" ta="center">
|
<Text c="blue" fw={600} fz="sm" ta="center">
|
||||||
{v.periode}
|
{v.periode}
|
||||||
</Text>
|
</Text>
|
||||||
</Tooltip>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -1,167 +1,3 @@
|
|||||||
// 'use client'
|
|
||||||
// import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
|
|
||||||
// import colors from '@/con/colors';
|
|
||||||
// import { Box, Grid, GridCol, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
|
|
||||||
// import { useProxy } from 'valtio/utils';
|
|
||||||
// import BackButton from '../../desa/layanan/_com/BackButto';
|
|
||||||
// import { useShallowEffect } from '@mantine/hooks';
|
|
||||||
|
|
||||||
|
|
||||||
// function Page() {
|
|
||||||
// const state = useProxy(PendapatanAsliDesa.ApbDesa);
|
|
||||||
|
|
||||||
// useShallowEffect(() => {
|
|
||||||
// state.findMany.load();
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// useShallowEffect(() => {
|
|
||||||
// PendapatanAsliDesa.pembiayaan.findMany.load();
|
|
||||||
// PendapatanAsliDesa.belanja.findMany.load();
|
|
||||||
// PendapatanAsliDesa.pendapatan.findMany.load();
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// // Get the latest APB data
|
|
||||||
// const latestApb = state.findMany.data?.[0];
|
|
||||||
|
|
||||||
// // Calculate totals
|
|
||||||
// const totalPendapatan = latestApb?.pendapatan?.reduce((sum, item) => sum + (item?.value || 0), 0) || 0;
|
|
||||||
// const totalBelanja = latestApb?.belanja?.reduce((sum, item) => sum + (item?.value || 0), 0) || 0;
|
|
||||||
// const totalPembiayaan = latestApb?.pembiayaan?.reduce((sum, item) => sum + (item?.value || 0), 0) || 0;
|
|
||||||
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <Stack pos="relative" bg={colors.Bg} py="xl" gap="lg">
|
|
||||||
// <Box px={{ base: 'md', md: 100 }}>
|
|
||||||
// <BackButton />
|
|
||||||
// </Box>
|
|
||||||
// <Text ta="center" fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw="bold">
|
|
||||||
// Pendapatan Asli Desa
|
|
||||||
// </Text>
|
|
||||||
// <Box px={{ base: "md", md: 100 }}>
|
|
||||||
// <Stack gap="lg" justify="center">
|
|
||||||
// <Paper bg={colors['white-1']} p="xl">
|
|
||||||
// <SimpleGrid cols={{ base: 1, md: 3 }} spacing="md">
|
|
||||||
// {/* Pendapatan Card */}
|
|
||||||
// <Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
|
||||||
// <Stack gap={"xs"}>
|
|
||||||
// <Title order={3}>Pendapatan</Title>
|
|
||||||
// {PendapatanAsliDesa.pendapatan.findMany.data?.map((item) => (
|
|
||||||
// <Box key={item.id}>
|
|
||||||
// <Grid>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="md" fw={500}>{item.name}</Text>
|
|
||||||
// </GridCol>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="md" fw={500}>{new Intl.NumberFormat('id-ID', {
|
|
||||||
// style: 'currency',
|
|
||||||
// currency: 'IDR',
|
|
||||||
// minimumFractionDigits: 0
|
|
||||||
// }).format(item.value)}</Text>
|
|
||||||
// </GridCol>
|
|
||||||
// </Grid>
|
|
||||||
// </Box>
|
|
||||||
// ))}
|
|
||||||
// <Grid>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="lg" fw={600} mb="xs">Total Pendapatan</Text>
|
|
||||||
// </GridCol>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="xl" fw={700} c={colors['blue-button']}>
|
|
||||||
// {new Intl.NumberFormat('id-ID', {
|
|
||||||
// style: 'currency',
|
|
||||||
// currency: 'IDR',
|
|
||||||
// minimumFractionDigits: 0
|
|
||||||
// }).format(totalPendapatan)}
|
|
||||||
// </Text>
|
|
||||||
// </GridCol>
|
|
||||||
// </Grid>
|
|
||||||
// </Stack>
|
|
||||||
// </Box>
|
|
||||||
|
|
||||||
// {/* Belanja Card */}
|
|
||||||
// <Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
|
||||||
// <Stack gap={"xs"}>
|
|
||||||
// <Title order={3}>Belanja</Title>
|
|
||||||
// {PendapatanAsliDesa.belanja.findMany.data?.map((item) => (
|
|
||||||
// <Box key={item.id}>
|
|
||||||
// <Grid>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="md" fw={500}>{item.name}</Text>
|
|
||||||
// </GridCol>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="md" fw={500}>{new Intl.NumberFormat('id-ID', {
|
|
||||||
// style: 'currency',
|
|
||||||
// currency: 'IDR',
|
|
||||||
// minimumFractionDigits: 0
|
|
||||||
// }).format(item.value)}</Text>
|
|
||||||
// </GridCol>
|
|
||||||
// </Grid>
|
|
||||||
// </Box>
|
|
||||||
// ))}
|
|
||||||
// <Grid>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="lg" fw={600} mb="xs">Total Belanja</Text>
|
|
||||||
// </GridCol>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="xl" fw={700} c="orange">
|
|
||||||
// {new Intl.NumberFormat('id-ID', {
|
|
||||||
// style: 'currency',
|
|
||||||
// currency: 'IDR',
|
|
||||||
// minimumFractionDigits: 0
|
|
||||||
// }).format(totalBelanja)}
|
|
||||||
// </Text>
|
|
||||||
// </GridCol>
|
|
||||||
// </Grid>
|
|
||||||
// </Stack>
|
|
||||||
// </Box>
|
|
||||||
|
|
||||||
// {/* Pembiayaan Card */}
|
|
||||||
// <Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
|
||||||
// <Stack gap={"xs"}>
|
|
||||||
// <Title order={3}>Pembiayaan</Title>
|
|
||||||
// {PendapatanAsliDesa.pembiayaan.findMany.data?.map((item) => (
|
|
||||||
// <Box key={item.id}>
|
|
||||||
// <Grid>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="md" fw={500}>{item.name}</Text>
|
|
||||||
// </GridCol>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="md" fw={500}>{new Intl.NumberFormat('id-ID', {
|
|
||||||
// style: 'currency',
|
|
||||||
// currency: 'IDR',
|
|
||||||
// minimumFractionDigits: 0
|
|
||||||
// }).format(item.value)}</Text>
|
|
||||||
// </GridCol>
|
|
||||||
// </Grid>
|
|
||||||
// </Box>
|
|
||||||
// ))}
|
|
||||||
// <Grid>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="lg" fw={600} mb="xs">Total Pembiayaan</Text>
|
|
||||||
// </GridCol>
|
|
||||||
// <GridCol span={{ base: 12, md: 6 }}>
|
|
||||||
// <Text fz="xl" fw={700} c="green">
|
|
||||||
// {new Intl.NumberFormat('id-ID', {
|
|
||||||
// style: 'currency',
|
|
||||||
// currency: 'IDR',
|
|
||||||
// minimumFractionDigits: 0
|
|
||||||
// }).format(totalPembiayaan)}
|
|
||||||
// </Text>
|
|
||||||
// </GridCol>
|
|
||||||
// </Grid>
|
|
||||||
// </Stack>
|
|
||||||
// </Box>
|
|
||||||
|
|
||||||
// </SimpleGrid>
|
|
||||||
// </Paper>
|
|
||||||
// </Stack>
|
|
||||||
// </Box>
|
|
||||||
// </Stack>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export default Page;
|
|
||||||
|
|
||||||
'use client'
|
'use client'
|
||||||
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
|
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
@@ -210,28 +46,37 @@ function Page() {
|
|||||||
<Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
<Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
<Title order={3}>Pendapatan</Title>
|
<Title order={3}>Pendapatan</Title>
|
||||||
{PendapatanAsliDesa.pendapatan.findMany.data?.map((item) => (
|
{latestApb?.pendapatan?.map((item) => (
|
||||||
<Box key={item.id}>
|
<Box key={item.id}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }} style={{ maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
<Text fz="md" fw={500}>{item.name}</Text>
|
<Text fz="md" fw={500}>{item.name}</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }} style={{ maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
<Text fz="md" fw={500}>{new Intl.NumberFormat('id-ID', {
|
<Text
|
||||||
style: 'currency',
|
fz="md"
|
||||||
currency: 'IDR',
|
fw={500}
|
||||||
minimumFractionDigits: 0
|
style={{
|
||||||
}).format(item.value)}</Text>
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textAlign: 'right',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(item.value)}
|
||||||
|
</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
<Grid>
|
<Grid>
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }} style={{ maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
<Text fz="lg" fw={600} mb="xs">Total Pendapatan</Text>
|
<Text fz="lg" fw={600} mb="xs">Total Pendapatan</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }} style={{ maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
<Text fz="xl" fw={700} c={colors['blue-button']}>
|
<Text style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal'
|
||||||
|
}} fz="xl" fw={700} c={colors['blue-button']}>
|
||||||
{new Intl.NumberFormat('id-ID', {
|
{new Intl.NumberFormat('id-ID', {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'IDR',
|
currency: 'IDR',
|
||||||
@@ -247,18 +92,28 @@ function Page() {
|
|||||||
<Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
<Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
<Title order={3}>Belanja</Title>
|
<Title order={3}>Belanja</Title>
|
||||||
{PendapatanAsliDesa.belanja.findMany.data?.map((item) => (
|
{latestApb?.belanja?.map((item) => (
|
||||||
<Box key={item.id}>
|
<Box key={item.id}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }} style={{ maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
<Text fz="md" fw={500}>{item.name}</Text>
|
<Text fz="md" fw={500}>{item.name}</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }} style={{ maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
<Text fz="md" fw={500}>{new Intl.NumberFormat('id-ID', {
|
<Text
|
||||||
|
fz="md"
|
||||||
|
fw={500}
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textAlign: 'right',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{new Intl.NumberFormat('id-ID', {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'IDR',
|
currency: 'IDR',
|
||||||
minimumFractionDigits: 0
|
minimumFractionDigits: 0
|
||||||
}).format(item.value)}</Text>
|
}).format(item.value)}
|
||||||
|
</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -284,18 +139,28 @@ function Page() {
|
|||||||
<Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
<Box p="md" style={{ border: '1px solid #e9ecef', borderRadius: '8px' }}>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
<Title order={3}>Pembiayaan</Title>
|
<Title order={3}>Pembiayaan</Title>
|
||||||
{PendapatanAsliDesa.pembiayaan.findMany.data?.map((item) => (
|
{latestApb?.pembiayaan?.map((item) => (
|
||||||
<Box key={item.id}>
|
<Box key={item.id}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }} style={{ maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
<Text fz="md" fw={500}>{item.name}</Text>
|
<Text fz="md" fw={500}>{item.name}</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
<GridCol span={{ base: 12, md: 6 }}>
|
<GridCol span={{ base: 12, md: 6 }} style={{ maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
<Text fz="md" fw={500}>{new Intl.NumberFormat('id-ID', {
|
<Text
|
||||||
|
fz="md"
|
||||||
|
fw={500}
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textAlign: 'right',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{new Intl.NumberFormat('id-ID', {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'IDR',
|
currency: 'IDR',
|
||||||
minimumFractionDigits: 0
|
minimumFractionDigits: 0
|
||||||
}).format(item.value)}</Text>
|
}).format(item.value)}
|
||||||
|
</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -367,4 +232,3 @@ function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import lowonganKerjaState from '@/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Center, Group, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconArrowBack, IconBrandWhatsapp, IconBriefcase, IconCurrencyDollar, IconMapPin, IconPhone } from '@tabler/icons-react';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
function DetailLowonganKerjaUser() {
|
||||||
|
const state = useProxy(lowonganKerjaState);
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useParams();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
const loadData = async () => {
|
||||||
|
await state.findUnique.load(params?.id as string);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
loadData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const data = state.findUnique.data;
|
||||||
|
|
||||||
|
if (loading || !data) {
|
||||||
|
return (
|
||||||
|
<Center py="xl">
|
||||||
|
<Skeleton height={500} w={{ base: '90%', md: '70%' }} radius="lg" />
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack bg={colors.Bg} py="xl" px={{ base: 'md', md: 100 }} align="center">
|
||||||
|
<Box w={{ base: '100%', md: '70%' }}>
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
color="blue"
|
||||||
|
leftSection={<IconArrowBack size={20} />}
|
||||||
|
mb="md"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
>
|
||||||
|
Kembali
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Paper
|
||||||
|
radius="lg"
|
||||||
|
shadow="md"
|
||||||
|
withBorder
|
||||||
|
p="xl"
|
||||||
|
bg={colors['white-1']}
|
||||||
|
>
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Judul */}
|
||||||
|
<Text fz={{ base: '1.6rem', md: '2rem' }} fw={700} c={colors['blue-button']}>
|
||||||
|
{data.posisi}
|
||||||
|
</Text>
|
||||||
|
<Text c="dimmed" fz="sm">
|
||||||
|
Diposting: {new Date(data.createdAt).toLocaleDateString('id-ID', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric'
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{/* Info Ringkas */}
|
||||||
|
<Stack gap="sm" mt="md">
|
||||||
|
<Group gap="xs">
|
||||||
|
<IconBriefcase size={20} color={colors['blue-button']} />
|
||||||
|
<Text fz="md" fw={600}>{data.namaPerusahaan}</Text>
|
||||||
|
</Group>
|
||||||
|
<Group gap="xs">
|
||||||
|
<IconMapPin size={20} color={colors['blue-button']} />
|
||||||
|
<Text fz="md">{data.lokasi}</Text>
|
||||||
|
</Group>
|
||||||
|
<Group gap="xs">
|
||||||
|
<IconPhone size={20} color={colors['blue-button']} />
|
||||||
|
<Text fz="md">{data.notelp}</Text>
|
||||||
|
</Group>
|
||||||
|
<Group gap="xs">
|
||||||
|
<IconCurrencyDollar size={20} color={colors['blue-button']} />
|
||||||
|
<Text fz="md">{data.gaji || '-'}</Text>
|
||||||
|
</Group>
|
||||||
|
<Group gap="xs">
|
||||||
|
<IconBriefcase size={20} color={colors['blue-button']} />
|
||||||
|
<Text fz="md">{data.tipePekerjaan}</Text>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fw={600} fz="lg" mb={4}>
|
||||||
|
Deskripsi Pekerjaan
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
fz="sm"
|
||||||
|
lh={1.6}
|
||||||
|
style={{ wordBreak: 'break-word' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.deskripsi || '-' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fw={600} fz="lg" mb={4}>
|
||||||
|
Kualifikasi
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
fz="sm"
|
||||||
|
lh={1.6}
|
||||||
|
style={{ wordBreak: 'break-word' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.kualifikasi || '-' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Center>
|
||||||
|
<Button
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
mt="md"
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
onClick={() => window.open(`https://wa.me/${data.notelp}`, '_blank')}
|
||||||
|
leftSection={<IconBrandWhatsapp size={20} />}
|
||||||
|
>
|
||||||
|
Hubungi Sekarang
|
||||||
|
</Button>
|
||||||
|
</Center>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailLowonganKerjaUser;
|
||||||
@@ -52,7 +52,7 @@ function Page() {
|
|||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }} pb={80}>
|
||||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||||
Lowongan Kerja Lokal
|
Lowongan Kerja Lokal
|
||||||
</Text>
|
</Text>
|
||||||
@@ -103,7 +103,7 @@ function Page() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
<Button onClick={() => router.push(`https://wa.me/${v.notelp?.replace(/\D/g, '')}`)}>Lamar Sekarang</Button>
|
<Button onClick={() => router.push(`/darmasaba/ekonomi/lowongan-kerja-lokal/${v.id}`)}>Detail</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
)
|
)
|
||||||
|
|||||||
157
src/app/darmasaba/(pages)/ekonomi/pasar-desa/[id]/page.tsx
Normal file
157
src/app/darmasaba/(pages)/ekonomi/pasar-desa/[id]/page.tsx
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Paper, Stack, Text, Image, Skeleton, Group, Badge, Divider } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconMapPin, IconPhone, IconStar } from '@tabler/icons-react';
|
||||||
|
import { useRouter, useParams } from 'next/navigation';
|
||||||
|
import React from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa';
|
||||||
|
|
||||||
|
function DetailProdukPasarUser() {
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useParams();
|
||||||
|
const statePasar = useProxy(pasarDesaState);
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
statePasar.pasarDesa.findUnique.load(params?.id as string);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const data = statePasar.pasarDesa.findUnique.data;
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
<Skeleton height={400} radius="md" />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box py={20}>
|
||||||
|
{/* Tombol kembali */}
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
leftSection={<IconArrowBack size={20} color={colors['blue-button']} />}
|
||||||
|
mb={15}
|
||||||
|
>
|
||||||
|
Kembali ke daftar produk
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper
|
||||||
|
w={{ base: '100%', md: '70%' }}
|
||||||
|
mx="auto"
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
bg={colors['white-1']}
|
||||||
|
>
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Gambar Produk */}
|
||||||
|
{data.image?.link ? (
|
||||||
|
<Image
|
||||||
|
src={data.image.link}
|
||||||
|
alt={data.nama}
|
||||||
|
radius="md"
|
||||||
|
h={250}
|
||||||
|
w="100%"
|
||||||
|
fit="cover"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
h={300}
|
||||||
|
bg="gray.1"
|
||||||
|
display="flex"
|
||||||
|
style={{ alignItems: 'center', justifyContent: 'center', borderRadius: 'md' }}
|
||||||
|
>
|
||||||
|
<Text c="dimmed">Tidak ada gambar</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Detail Produk */}
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Text fz="2xl" fw="bold" c={colors['blue-button']}>
|
||||||
|
{data.nama || 'Produk Tanpa Nama'}
|
||||||
|
</Text>
|
||||||
|
<Group>
|
||||||
|
<Badge color="green" size="lg" radius="md">
|
||||||
|
Rp {data.harga?.toLocaleString('id-ID')}
|
||||||
|
</Badge>
|
||||||
|
{data.rating && (
|
||||||
|
<Group gap={4}>
|
||||||
|
<IconStar size={18} color="#FFD43B" />
|
||||||
|
<Text fz="md" fw={500}>{data.rating}</Text>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Divider my="sm" />
|
||||||
|
|
||||||
|
{/* Info Tambahan */}
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw={600}>Kategori</Text>
|
||||||
|
<Group gap="xs" mt={4}>
|
||||||
|
{data.KategoriToPasar && data.KategoriToPasar.length > 0 ? (
|
||||||
|
data.KategoriToPasar.map((kategori) => (
|
||||||
|
<Badge key={kategori.id} color="blue" variant="light">
|
||||||
|
{kategori.kategori.nama}
|
||||||
|
</Badge>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Text fz="sm" c="dimmed">Tidak ada kategori</Text>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{data.alamatUsaha && (
|
||||||
|
<Group gap={6}>
|
||||||
|
<IconMapPin size={18} color={colors['blue-button']} />
|
||||||
|
<Text fz="md">{data.alamatUsaha}</Text>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{data.kontak && (
|
||||||
|
<Group gap={6}>
|
||||||
|
<IconPhone size={18} color={colors['blue-button']} />
|
||||||
|
<Text fz="md">{data.kontak}</Text>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Divider my="sm" />
|
||||||
|
|
||||||
|
{/* Deskripsi */}
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw={600}>Deskripsi Produk</Text>
|
||||||
|
<Text fz="md" c="dimmed" mt={4}>
|
||||||
|
Tidak ada deskripsi.
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Tombol Aksi User */}
|
||||||
|
{data.kontak && (
|
||||||
|
<Button
|
||||||
|
mt="md"
|
||||||
|
color="green"
|
||||||
|
size="lg"
|
||||||
|
radius="md"
|
||||||
|
component="a"
|
||||||
|
href={`https://wa.me/${data.kontak.replace(/[^0-9]/g, '')}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Hubungi Penjual via WhatsApp
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailProdukPasarUser;
|
||||||
@@ -71,8 +71,11 @@ function Page() {
|
|||||||
/>
|
/>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz={{ base: "h4", md: "h3" }} >
|
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz="md" >
|
||||||
Pasar Desa Online merupakan Media Promosi yang bertujuan untuk membantu warga desa dalam memasarkan dan memperkenalkan produknya kepada masyarakat.
|
Pasar Desa Online adalah media promosi untuk membantu warga memasarkan
|
||||||
|
</Text>
|
||||||
|
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz="md" >
|
||||||
|
dan memperkenalkan produk mereka.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
<Box px={{ base: "md", md: 100 }}>
|
||||||
@@ -105,7 +108,7 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Stack key={k}>
|
<Stack key={k}>
|
||||||
<motion.div
|
<motion.div
|
||||||
onClick={() => router.push(`https://wa.me/${v.kontak?.replace(/\D/g, '')}`)}
|
onClick={() => router.push(`/darmasaba/ekonomi/pasar-desa/${v.id}`)}
|
||||||
whileHover={{ scale: 1.05 }}
|
whileHover={{ scale: 1.05 }}
|
||||||
whileTap={{ scale: 0.8 }}
|
whileTap={{ scale: 0.8 }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'
|
import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'
|
||||||
import colors from '@/con/colors'
|
import colors from '@/con/colors'
|
||||||
import {
|
import {
|
||||||
@@ -9,20 +9,28 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Center,
|
Center,
|
||||||
Container,
|
|
||||||
Group,
|
Group,
|
||||||
Image,
|
Image,
|
||||||
Loader,
|
Loader,
|
||||||
Paper,
|
Paper,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
Tooltip,
|
Transition
|
||||||
Transition,
|
|
||||||
} from '@mantine/core'
|
} from '@mantine/core'
|
||||||
import { IconRefresh, IconSearch, IconUsers } from '@tabler/icons-react'
|
import {
|
||||||
|
IconArrowsMaximize,
|
||||||
|
IconArrowsMinimize,
|
||||||
|
IconRefresh,
|
||||||
|
IconSearch,
|
||||||
|
IconUsers,
|
||||||
|
IconZoomIn,
|
||||||
|
IconZoomOut,
|
||||||
|
} from '@tabler/icons-react'
|
||||||
|
import { debounce } from 'lodash'
|
||||||
import { OrganizationChart } from 'primereact/organizationchart'
|
import { OrganizationChart } from 'primereact/organizationchart'
|
||||||
import { useEffect } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useProxy } from 'valtio/utils'
|
import { useProxy } from 'valtio/utils'
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto'
|
import BackButton from '../../desa/layanan/_com/BackButto'
|
||||||
|
|
||||||
@@ -36,35 +44,40 @@ export default function Page() {
|
|||||||
paddingBottom: 48,
|
paddingBottom: 48,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container size="xl" py="xl">
|
<Box px={{ base: 'md', md: 100 }} py="xl">
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
|
||||||
<Stack align="center" gap="xl" mt="xl">
|
<Stack align="center" gap="xl" mt="xl">
|
||||||
<Title
|
<Title
|
||||||
order={1}
|
order={1}
|
||||||
ta="center"
|
ta="center"
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
fz={{ base: 28, md: 36, lg: 44 }}
|
fz={{ base: 28, md: 36, lg: 44 }}
|
||||||
|
|
||||||
>
|
>
|
||||||
Struktur Organisasi Dan SK Pengurus BumDes
|
Struktur Organisasi & SK Pengurus BumDes
|
||||||
</Title>
|
</Title>
|
||||||
<Text ta="center" c="black" maw={800}>
|
<Text ta="center" c="black" maw={800}>
|
||||||
Gambaran visual peran dan pegawai yang ditugaskan. Arahkan kursor
|
Gambaran visual peran dan pengurus yang ditugaskan. Gunakan kontrol
|
||||||
untuk melihat detail atau klik node untuk fokus tampilan.
|
di bawah untuk mencari, memperbesar, atau melihat lebih jelas.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Box mt="lg">
|
<Box mt="lg">
|
||||||
<StrukturOrganisasiBumDes />
|
<StrukturOrganisasiBumDes />
|
||||||
</Box>
|
</Box>
|
||||||
</Container>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function StrukturOrganisasiBumDes() {
|
function StrukturOrganisasiBumDes() {
|
||||||
const stateOrganisasi: any = useProxy(stateStrukturBumDes.pegawai)
|
const stateOrganisasi: any = useProxy(stateStrukturBumDes.pegawai)
|
||||||
|
const chartContainerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const [scale, setScale] = useState(1)
|
||||||
|
const [isFullscreen, setFullscreen] = useState(false)
|
||||||
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
|
const debouncedSearch = useRef(
|
||||||
|
debounce((value: string) => setSearchQuery(value), 400)
|
||||||
|
).current
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void stateOrganisasi.findMany.load()
|
void stateOrganisasi.findMany.load()
|
||||||
@@ -81,17 +94,15 @@ function StrukturOrganisasiBumDes() {
|
|||||||
<Loader size="lg" />
|
<Loader size="lg" />
|
||||||
<Text fw={600}>Memuat struktur organisasi…</Text>
|
<Text fw={600}>Memuat struktur organisasi…</Text>
|
||||||
<Text c="dimmed" size="sm">
|
<Text c="dimmed" size="sm">
|
||||||
Mengambil data pegawai dan posisi. Mohon tunggu sebentar.
|
Mengambil data pengurus dan posisi. Mohon tunggu sebentar.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const data = stateOrganisasi.findMany.data || []
|
||||||
!stateOrganisasi.findMany.data ||
|
if (data.length === 0) {
|
||||||
stateOrganisasi.findMany.data.length === 0
|
|
||||||
) {
|
|
||||||
return (
|
return (
|
||||||
<Center py={40}>
|
<Center py={40}>
|
||||||
<Stack align="center" gap="md">
|
<Stack align="center" gap="md">
|
||||||
@@ -109,11 +120,10 @@ function StrukturOrganisasiBumDes() {
|
|||||||
<IconUsers size={56} />
|
<IconUsers size={56} />
|
||||||
</Center>
|
</Center>
|
||||||
<Title order={3} mt="md">
|
<Title order={3} mt="md">
|
||||||
Data pegawai belum tersedia
|
Data pengurus belum tersedia
|
||||||
</Title>
|
</Title>
|
||||||
<Text c="dimmed" mt="xs">
|
<Text c="dimmed" mt="xs">
|
||||||
Belum ada data pegawai yang tercatat untuk BumDes. Silakan coba
|
Belum ada data pengurus yang tercatat untuk BumDes.
|
||||||
muat ulang atau periksa sumber data.
|
|
||||||
</Text>
|
</Text>
|
||||||
<Group justify="center" mt="lg">
|
<Group justify="center" mt="lg">
|
||||||
<Button
|
<Button
|
||||||
@@ -124,15 +134,6 @@ function StrukturOrganisasiBumDes() {
|
|||||||
>
|
>
|
||||||
Muat Ulang
|
Muat Ulang
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
leftSection={<IconSearch size={16} />}
|
|
||||||
variant="subtle"
|
|
||||||
onClick={() =>
|
|
||||||
stateOrganisasi.findMany.load({ query: { q: '' } })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Cari Pegawai
|
|
||||||
</Button>
|
|
||||||
</Group>
|
</Group>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -140,161 +141,232 @@ function StrukturOrganisasiBumDes() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 📊 susun struktur organisasi
|
||||||
const posisiMap = new Map<string, any>()
|
const posisiMap = new Map<string, any>()
|
||||||
|
const aktifPegawai = data.filter((p: any) => p.isActive)
|
||||||
const aktifPegawai = stateOrganisasi.findMany.data.filter((p: any) => p.isActive);
|
|
||||||
|
|
||||||
for (const pegawai of aktifPegawai) {
|
for (const pegawai of aktifPegawai) {
|
||||||
const posisiId = pegawai.posisi.id;
|
const posisiId = pegawai.posisi.id
|
||||||
if (!posisiMap.has(posisiId)) {
|
if (!posisiMap.has(posisiId)) {
|
||||||
posisiMap.set(posisiId, {
|
posisiMap.set(posisiId, {
|
||||||
...pegawai.posisi,
|
...pegawai.posisi,
|
||||||
pegawaiList: [],
|
pegawaiList: [],
|
||||||
children: [],
|
children: [],
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
posisiMap.get(posisiId)!.pegawaiList.push(pegawai);
|
posisiMap.get(posisiId)!.pegawaiList.push(pegawai)
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, create a map of all unique positions
|
const root: any[] = []
|
||||||
const allPositions = new Map();
|
posisiMap.forEach((posisi) => {
|
||||||
aktifPegawai.forEach((pegawai: any) => {
|
|
||||||
if (!allPositions.has(pegawai.posisi.id)) {
|
|
||||||
allPositions.set(pegawai.posisi.id, {
|
|
||||||
...pegawai.posisi,
|
|
||||||
pegawaiList: [],
|
|
||||||
children: []
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Then assign employees to their positions
|
|
||||||
aktifPegawai.forEach((pegawai: any) => {
|
|
||||||
const posisi = allPositions.get(pegawai.posisi.id);
|
|
||||||
if (posisi) {
|
|
||||||
posisi.pegawaiList.push(pegawai);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Now build the hierarchy
|
|
||||||
const root = [];
|
|
||||||
for (const [_, posisi] of allPositions) {
|
|
||||||
if (posisi.parentId) {
|
if (posisi.parentId) {
|
||||||
const parent = allPositions.get(posisi.parentId);
|
const parent = posisiMap.get(posisi.parentId)
|
||||||
if (parent) {
|
if (parent) parent.children.push(posisi)
|
||||||
parent.children.push(posisi);
|
else root.push(posisi)
|
||||||
} else {
|
} else root.push(posisi)
|
||||||
// Only add to root if it's a top-level position
|
})
|
||||||
if (!posisi.parentId) {
|
|
||||||
root.push(posisi);
|
const toOrgChartFormat = (node: any): any => {
|
||||||
}
|
const pegawai = node.pegawaiList?.[0]
|
||||||
}
|
|
||||||
} else {
|
|
||||||
root.push(posisi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function toOrgChartFormat(node: any): any {
|
|
||||||
return {
|
return {
|
||||||
expanded: true,
|
expanded: true,
|
||||||
type: 'person',
|
|
||||||
styleClass: 'p-person',
|
|
||||||
data: {
|
data: {
|
||||||
name: node.pegawaiList?.[0]?.namaLengkap || 'Belum ditugaskan',
|
id: pegawai?.id,
|
||||||
title: node.nama || 'Tanpa jabatan',
|
name: pegawai?.namaLengkap || 'Belum Ditugaskan',
|
||||||
image: node.pegawaiList?.[0]?.image?.link || '/img/default.png',
|
title: node.nama || 'Tanpa Jabatan',
|
||||||
|
image: pegawai?.image?.link || '/img/default.png',
|
||||||
description: node.deskripsi || '',
|
description: node.deskripsi || '',
|
||||||
positionId: node.id || null,
|
|
||||||
},
|
},
|
||||||
children: node.children?.map(toOrgChartFormat) || [],
|
children: node.children?.map(toOrgChartFormat) || [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chartData = root.map(toOrgChartFormat)
|
let chartData = root.map(toOrgChartFormat)
|
||||||
|
|
||||||
|
// 🔍 filter by search
|
||||||
|
if (searchQuery) {
|
||||||
|
const filterNodes = (nodes: any[]): any[] =>
|
||||||
|
nodes
|
||||||
|
.map((n) => ({
|
||||||
|
...n,
|
||||||
|
children: filterNodes(n.children || []),
|
||||||
|
}))
|
||||||
|
.filter(
|
||||||
|
(n) =>
|
||||||
|
n.data.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
n.data.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
n.children.length > 0
|
||||||
|
)
|
||||||
|
chartData = filterNodes(chartData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔍 fullscreen dan zoom control
|
||||||
|
const toggleFullscreen = () => {
|
||||||
|
if (!document.fullscreenElement) {
|
||||||
|
chartContainerRef.current?.requestFullscreen()
|
||||||
|
setFullscreen(true)
|
||||||
|
} else {
|
||||||
|
document.exitFullscreen()
|
||||||
|
setFullscreen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleZoomIn = () => setScale((s) => Math.min(s + 0.1, 2))
|
||||||
|
const handleZoomOut = () => setScale((s) => Math.max(s - 0.1, 0.5))
|
||||||
|
const resetZoom = () => setScale(1)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={16} >
|
<Stack align="center" mt="xl">
|
||||||
<Paper
|
{/* 🧭 Kontrol atas */}
|
||||||
radius="md"
|
<Paper shadow="xs" p="md" radius="md" bg={colors['blue-button']}>
|
||||||
p="md"
|
<Group gap="sm" wrap="wrap" justify="center">
|
||||||
|
<TextInput
|
||||||
|
placeholder="Cari nama atau jabatan..."
|
||||||
|
leftSection={<IconSearch size={16} />}
|
||||||
|
onChange={(e) => debouncedSearch(e.target.value)}
|
||||||
|
styles={{
|
||||||
|
input: {
|
||||||
|
minWidth: 250,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Group gap="xs">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
bg={colors['blue-button-2']}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
size="sm"
|
||||||
|
onClick={handleZoomOut}
|
||||||
|
leftSection={<IconZoomOut size={16} />}
|
||||||
|
>
|
||||||
|
Zoom Out
|
||||||
|
</Button>
|
||||||
|
<Box
|
||||||
|
bg={colors['blue-button-2']}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
px={16}
|
||||||
|
py={8}
|
||||||
|
style={{
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 700,
|
||||||
|
borderRadius: '8px',
|
||||||
|
minWidth: 70,
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Math.round(scale * 100)}%
|
||||||
|
</Box>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
bg={colors['blue-button-2']}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
size="sm"
|
||||||
|
onClick={handleZoomIn}
|
||||||
|
leftSection={<IconZoomIn size={16} />}
|
||||||
|
>
|
||||||
|
Zoom In
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
bg={colors['blue-button-2']}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
size="sm"
|
||||||
|
onClick={resetZoom}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
bg={colors['blue-button-2']}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
size="sm"
|
||||||
|
onClick={toggleFullscreen}
|
||||||
|
leftSection={
|
||||||
|
isFullscreen ? (
|
||||||
|
<IconArrowsMinimize size={16} />
|
||||||
|
) : (
|
||||||
|
<IconArrowsMaximize size={16} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Fullscreen
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* 📊 Chart Container */}
|
||||||
|
<Center style={{ width: '100%' }}>
|
||||||
|
<Box
|
||||||
|
ref={chartContainerRef}
|
||||||
style={{
|
style={{
|
||||||
background: 'rgba(28,110,164,0.2)',
|
|
||||||
border: `1px solid rgba(255,255,255,0.1)`,
|
|
||||||
overflowX: 'auto',
|
overflowX: 'auto',
|
||||||
|
overflowY: 'auto',
|
||||||
|
width: '100%',
|
||||||
|
padding: '32px 16px',
|
||||||
|
transition: 'transform 0.2s ease',
|
||||||
|
transform: `scale(${scale})`,
|
||||||
|
transformOrigin: 'center top',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<OrganizationChart
|
<OrganizationChart
|
||||||
value={chartData}
|
value={chartData}
|
||||||
nodeTemplate={nodeTemplate}
|
nodeTemplate={(node) => <NodeCard node={node} />}
|
||||||
|
className="p-organizationchart p-organizationchart-horizontal"
|
||||||
/>
|
/>
|
||||||
</Paper>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
</Center>
|
||||||
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeTemplate(node: any) {
|
function NodeCard({ node }: any) {
|
||||||
const imageSrc = node?.data?.image || '/img/default.png'
|
const imageSrc = node?.data?.image || '/img/default.png'
|
||||||
const name = node?.data?.name || 'Tanpa Nama'
|
const name = node?.data?.name || 'Tanpa Nama'
|
||||||
const title = node?.data?.title || 'Tanpa Jabatan'
|
const title = node?.data?.title || 'Tanpa Jabatan'
|
||||||
const description = node?.data?.description || ''
|
const description = node?.data?.description || ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition mounted transition="pop" duration={240}>
|
<Transition mounted transition="pop" duration={300}>
|
||||||
{(styles) => (
|
{(styles) => (
|
||||||
<Card
|
<Card
|
||||||
radius="lg"
|
shadow="md"
|
||||||
|
radius="xl"
|
||||||
withBorder
|
withBorder
|
||||||
style={{
|
style={{
|
||||||
...styles,
|
...styles,
|
||||||
width: 260,
|
width: 240,
|
||||||
padding: 16,
|
padding: 20,
|
||||||
background: 'rgba(28,110,164,0.3)',
|
background:
|
||||||
borderColor: 'rgba(255,255,255,0.15)',
|
'linear-gradient(135deg, rgba(28,110,164,0.15) 0%, rgba(255,255,255,0.95) 100%)',
|
||||||
display: 'flex',
|
borderColor: 'rgba(28, 110, 164, 0.3)',
|
||||||
flexDirection: 'column',
|
transition: 'all 0.3s ease',
|
||||||
alignItems: 'center',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Stack align="center" gap={10}>
|
||||||
src={imageSrc}
|
<Box
|
||||||
alt={name}
|
|
||||||
radius="md"
|
|
||||||
width={120}
|
|
||||||
height={120}
|
|
||||||
fit="cover"
|
|
||||||
style={{
|
style={{
|
||||||
objectFit: 'cover',
|
width: 90,
|
||||||
border: '2px solid rgba(255,255,255,0.2)',
|
height: 90,
|
||||||
marginBottom: 12,
|
borderRadius: '50%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
border: '3px solid rgba(28, 110, 164, 0.4)',
|
||||||
}}
|
}}
|
||||||
loading='lazy'
|
>
|
||||||
/>
|
<Image src={imageSrc} alt={name} fit="cover" loading="lazy" />
|
||||||
<Text fw={700}>{name}</Text>
|
</Box>
|
||||||
<Text size="sm" c="dimmed" mt={4}>
|
<Text fw={700} size="sm" ta="center" c={colors['blue-button']}>
|
||||||
|
{name}
|
||||||
|
</Text>
|
||||||
|
<Text size="xs" c="dimmed" ta="center">
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs" c="dimmed" mt={8} lineClamp={3}>
|
<Text size="xs" c="dimmed" ta="center" lineClamp={3}>
|
||||||
{description || 'Belum ada deskripsi.'}
|
{description || 'Belum ada deskripsi.'}
|
||||||
</Text>
|
</Text>
|
||||||
<Tooltip label="Kembali ke struktur organisasi" withArrow position="bottom">
|
</Stack>
|
||||||
<Button
|
|
||||||
variant="light"
|
|
||||||
size="xs"
|
|
||||||
mt="md"
|
|
||||||
onClick={() => {
|
|
||||||
const id = node?.data?.positionId
|
|
||||||
if (id && (window as any).scrollTo) {
|
|
||||||
;(window as any).scrollTo({ top: 0, behavior: 'smooth' })
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Kembali
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
</Transition>
|
</Transition>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ function Page() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Paper p={'xl'} >
|
<Paper p={'xl'} >
|
||||||
|
<Stack gap={"xs"}>
|
||||||
<Text fz={'h3'} fw={'bold'} c={colors['blue-button']}>Tujuan Ide Inovatif Ini</Text>
|
<Text fz={'h3'} fw={'bold'} c={colors['blue-button']}>Tujuan Ide Inovatif Ini</Text>
|
||||||
<List>
|
<List>
|
||||||
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Mendorong partisipasi aktif masyarakat</ListItem>
|
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Mendorong partisipasi aktif masyarakat</ListItem>
|
||||||
@@ -62,6 +63,7 @@ function Page() {
|
|||||||
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Memecahkan tantangan komunal</ListItem>
|
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Memecahkan tantangan komunal</ListItem>
|
||||||
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Mengembangkan potensi kreativitas warga</ListItem>
|
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Mengembangkan potensi kreativitas warga</ListItem>
|
||||||
</List>
|
</List>
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Paper p={'xl'} >
|
<Paper p={'xl'} >
|
||||||
<Flex align={'center'} justify={'space-between'}>
|
<Flex align={'center'} justify={'space-between'}>
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ function Page() {
|
|||||||
/>
|
/>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Text fz={'h4'}>Mewujudkan Desa Darmasaba sebagai pusat inovasi digital yang memberdayakan masyarakat, meningkatkan kesejahteraan, dan menciptakan peluang ekonomi berbasis teknologi.</Text>
|
<Text fz={'md'}>Menjadikan Desa Darmasaba pusat inovasi digital untuk pemberdayaan masyarakat</Text>
|
||||||
|
<Text fz={'md'}>dan peningkatan ekonomi berbasis teknologi.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
<Box px={{ base: "md", md: 100 }}>
|
||||||
<Stack gap={'lg'} justify='center'>
|
<Stack gap={'lg'} justify='center'>
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ function Page() {
|
|||||||
/>
|
/>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Text fz={'h4'}>Desa Darmasaba berkomitmen mengembangkan teknologi tepat guna yang sesuai dengan kebutuhan masyarakat, mendukung pembangunan berkelanjutan, dan meningkatkan kualitas hidup warga.</Text>
|
<Text fz={'md'}>Desa Darmasaba berkomitmen mengembangkan teknologi tepat guna yang sesuai dengan kebutuhan masyarakat,</Text>
|
||||||
|
<Text fz={'md'}>mendukung pembangunan berkelanjutan, dan meningkatkan kualitas hidup warga.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
<Box px={{ base: "md", md: 100 }}>
|
||||||
<Stack gap={'lg'} p={'lg'}>
|
<Stack gap={'lg'} p={'lg'}>
|
||||||
@@ -71,11 +72,22 @@ function Page() {
|
|||||||
{filteredData.map((v, k) => {
|
{filteredData.map((v, k) => {
|
||||||
return (
|
return (
|
||||||
<Paper p={'xl'} key={k}>
|
<Paper p={'xl'} key={k}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
<Image src={v.image.link || ''} pb={10} radius={10} alt='' loading="lazy" />
|
<Image src={v.image.link || ''} pb={10} radius={10} alt='' loading="lazy" />
|
||||||
<Text fz={'h3'} fw={'bold'} c={colors['blue-button']}>{v.name}</Text>
|
<Text fz={'h3'} fw={'bold'}>{v.name}</Text>
|
||||||
<Box pr={'lg'} pb={10}>
|
<Box pr={'lg'} pb={10}>
|
||||||
<Text fz={'h4'} fw={'bold'} style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
|
<Text
|
||||||
|
size="md"
|
||||||
|
ta="justify"
|
||||||
|
lh={1} // line height biar enak dibaca
|
||||||
|
style={{
|
||||||
|
wordBreak: "break-word",
|
||||||
|
whiteSpace: "normal",
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -75,18 +75,18 @@ function AdministrasiOnline() {
|
|||||||
<Title order={3}>Ajukan Administrasi Online</Title>
|
<Title order={3}>Ajukan Administrasi Online</Title>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fz="sm" fw="bold">Nama</Text>}
|
label={<Text fz="sm" fw="bold">Nama</Text>}
|
||||||
placeholder="masukkan nama"
|
placeholder="Masukkan nama"
|
||||||
onChange={(val) => (state.administrasiOnline.create.form.name = val.target.value)}
|
onChange={(val) => (state.administrasiOnline.create.form.name = val.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fz="sm" fw="bold">Alamat</Text>}
|
label={<Text fz="sm" fw="bold">Alamat</Text>}
|
||||||
placeholder="masukkan alamat"
|
placeholder="Masukkan alamat"
|
||||||
onChange={(val) => (state.administrasiOnline.create.form.alamat = val.target.value)}
|
onChange={(val) => (state.administrasiOnline.create.form.alamat = val.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
type="number"
|
type="number"
|
||||||
label={<Text fz="sm" fw="bold">Nomor Telepon</Text>}
|
label={<Text fz="sm" fw="bold">Nomor Telepon</Text>}
|
||||||
placeholder="masukkan nomor telepon"
|
placeholder="Masukkan nomor telepon"
|
||||||
onChange={(val) => (state.administrasiOnline.create.form.nomorTelepon = val.target.value)}
|
onChange={(val) => (state.administrasiOnline.create.form.nomorTelepon = val.target.value)}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
@@ -95,7 +95,7 @@ function AdministrasiOnline() {
|
|||||||
state.administrasiOnline.create.form.jenisLayananId = val ?? "";
|
state.administrasiOnline.create.form.jenisLayananId = val ?? "";
|
||||||
}}
|
}}
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Jenis Layanan</Text>}
|
label={<Text fw={"bold"} fz={"sm"}>Jenis Layanan</Text>}
|
||||||
placeholder="Pilih kategori produk"
|
placeholder="Pilih jenis layanan"
|
||||||
data={
|
data={
|
||||||
state.jenisLayanan.findMany.data?.map((v) => ({
|
state.jenisLayanan.findMany.data?.map((v) => ({
|
||||||
value: v.id,
|
value: v.id,
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
'use client'
|
||||||
|
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||||
|
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';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Page() {
|
||||||
|
const params = useParams<{ id: string }>();
|
||||||
|
const id = Array.isArray(params.id) ? params.id[0] : params.id;
|
||||||
|
const state = useProxy(stateDashboardBerita.berita)
|
||||||
|
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} 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>
|
||||||
|
</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}
|
||||||
|
fz={{ base: "sm", md: "lg" }}
|
||||||
|
lh={{ base: 1.6, md: 1.8 }} // ✅ line-height lebih rapat dan responsif
|
||||||
|
ta="justify"
|
||||||
|
style={{
|
||||||
|
wordBreak: "break-word",
|
||||||
|
whiteSpace: "normal",
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: state.findUnique.data?.content || "",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
'use client'
|
||||||
|
import stateDesaPengumuman from '@/app/admin/(dashboard)/_state/desa/pengumuman';
|
||||||
|
import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Container, Group, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
|
||||||
|
function Page() {
|
||||||
|
const detail = useProxy(stateDesaPengumuman.pengumuman.findUnique)
|
||||||
|
|
||||||
|
const params = useParams()
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
stateDesaPengumuman.pengumuman.findUnique.load(params?.id as string)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!detail.data) {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Skeleton h={400} />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap="md">
|
||||||
|
{/* Header */}
|
||||||
|
<Box px={{ base: "md", md: 100 }}>
|
||||||
|
<BackButton />
|
||||||
|
</Box>
|
||||||
|
<Container size="lg" px="md">
|
||||||
|
<Stack gap="xs" >
|
||||||
|
<Group justify={"space-between"} align={"center"}>
|
||||||
|
<Text fz={{ base: "2rem", md: "2rem" }} c={colors["blue-button"]} fw="bold" >
|
||||||
|
{detail.data?.judul}
|
||||||
|
</Text>
|
||||||
|
<Group justify='end'>
|
||||||
|
<Paper bg={colors['blue-button']} p={5}>
|
||||||
|
<Text c={colors['white-1']}>{detail.data?.CategoryPengumuman?.name}</Text>
|
||||||
|
</Paper>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
<Paper bg={colors["white-1"]} p="md">
|
||||||
|
<Text fz={"md"} style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: detail.data?.content }} />
|
||||||
|
<Text fz={"md"} c={colors["blue-button"]} fw="bold" >
|
||||||
|
{new Date(detail.data?.createdAt).toLocaleDateString('id-ID', {
|
||||||
|
weekday: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric'
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -1,20 +1,23 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
import stateDashboardBerita from '@/app/admin/(dashboard)/_state/desa/berita';
|
||||||
|
import stateDesaPengumuman from '@/app/admin/(dashboard)/_state/desa/pengumuman';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Card, Divider, Grid, GridCol, Image, Paper, Stack, Text, Title } from '@mantine/core';
|
import { Badge, Box, Card, Divider, Grid, GridCol, Group, Image, Paper, Stack, Text, Title } from '@mantine/core';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import BackButton from '../../../desa/layanan/_com/BackButto';
|
import BackButton from '../../../desa/layanan/_com/BackButto';
|
||||||
import stateDesaPengumuman from '@/app/admin/(dashboard)/_state/desa/pengumuman';
|
import { useTransitionRouter } from 'next-view-transitions';
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
function InformasiDesa() {
|
function InformasiDesa() {
|
||||||
const stateBerita = useProxy(stateDashboardBerita.berita)
|
const router = useTransitionRouter()
|
||||||
const statePengumuman = useProxy(stateDesaPengumuman.pengumuman)
|
const stateBerita = useProxy(stateDashboardBerita.berita);
|
||||||
|
const statePengumuman = useProxy(stateDesaPengumuman.pengumuman);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
stateBerita.findFirst.load();
|
stateBerita.findFirst.load();
|
||||||
@@ -23,116 +26,216 @@ function InformasiDesa() {
|
|||||||
statePengumuman.findRecent.load();
|
statePengumuman.findRecent.load();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const dataBerita = stateBerita.findFirst.data
|
const dataBerita = stateBerita.findFirst.data;
|
||||||
const dataPengumuman = statePengumuman.findFirst.data
|
const dataPengumuman = statePengumuman.findFirst.data;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
<Title ta="center" fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
||||||
Informasi Desa
|
Informasi Desa
|
||||||
</Text>
|
</Title>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
|
||||||
<Stack gap={10}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Stack gap={30}>
|
||||||
|
|
||||||
|
{/* === BERITA UTAMA === */}
|
||||||
{dataBerita && (
|
{dataBerita && (
|
||||||
<Paper shadow="md" radius="md" p="md">
|
<motion.div
|
||||||
<Grid>
|
whileHover={{ scale: 1.05 }}
|
||||||
<GridCol span={{ md: 6, base: 12 }}>
|
whileTap={{ scale: 0.95 }}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onClick={() => router.push(`/darmasaba/inovasi/layanan-online-desa/informasi-desa/detail-berita/${dataBerita.id}`)}
|
||||||
|
>
|
||||||
|
<Paper shadow="md" radius="lg" p="lg" withBorder>
|
||||||
|
<Grid align="center" gutter="xl">
|
||||||
|
<GridCol span={{ base: 12, md: 6 }}>
|
||||||
<Image
|
<Image
|
||||||
src={dataBerita.image?.link || "/fallback.jpg"}
|
src={dataBerita.image?.link || '/fallback.jpg'}
|
||||||
alt={dataBerita.judul}
|
alt={dataBerita.judul}
|
||||||
radius="md"
|
radius="md"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
height={250}
|
height={280}
|
||||||
maw={600}
|
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
|
style={{ objectPosition: 'center' }}
|
||||||
/>
|
/>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
<GridCol span={{ md: 6, base: 12 }}>
|
<GridCol span={{ base: 12, md: 6 }}>
|
||||||
<Box>
|
<Stack gap="xs">
|
||||||
<Text fz="sm" c="dimmed">{dataBerita.kategoriBerita?.name} • {dayjs(dataBerita.createdAt).fromNow()}</Text>
|
<Title order={2} fw={800}>
|
||||||
<Title order={1} fw="bold">{dataBerita.judul}</Title>
|
{dataBerita.judul}
|
||||||
<Text ta={"justify"} mt="xs" fz="md" style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: dataBerita.content }} />
|
</Title>
|
||||||
</Box>
|
<Group justify='space-between'>
|
||||||
|
<Badge bg={colors['blue-button']}>
|
||||||
|
{dataBerita.kategoriBerita?.name}
|
||||||
|
</Badge>
|
||||||
|
<Text fz="sm" c="dimmed">
|
||||||
|
{dayjs(dataBerita.createdAt).fromNow()}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Text
|
||||||
|
ta="justify"
|
||||||
|
mt="xs"
|
||||||
|
fz="md"
|
||||||
|
lh={1.7}
|
||||||
|
lineClamp={6}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: dataBerita.content }}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
<Stack py={10}>
|
|
||||||
<Title order={3}>Berita Terbaru</Title>
|
{/* === BERITA TERBARU === */}
|
||||||
<Grid>
|
<Stack>
|
||||||
|
<Title order={3} fw={700}>
|
||||||
|
Berita Terbaru
|
||||||
|
</Title>
|
||||||
|
<Grid gutter="xl">
|
||||||
{stateBerita.findRecent.data.map((item) => (
|
{stateBerita.findRecent.data.map((item) => (
|
||||||
<GridCol span={{ base: 12, sm: 6, md: 3 }} key={item.id}>
|
<GridCol span={{ base: 12, sm: 6, md: 3 }} key={item.id}>
|
||||||
<Card shadow="sm" radius="md" withBorder h="100%">
|
<motion.div
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onClick={() => router.push(`/darmasaba/inovasi/layanan-online-desa/informasi-desa/detail-berita/${item.id}`)}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
shadow="sm"
|
||||||
|
radius="md"
|
||||||
|
withBorder
|
||||||
|
h="100%"
|
||||||
|
>
|
||||||
<Card.Section>
|
<Card.Section>
|
||||||
<Image
|
<Image
|
||||||
src={item.image?.link || "/placeholder.jpg"}
|
src={item.image?.link || '/placeholder.jpg'}
|
||||||
alt={item.judul}
|
alt={item.judul}
|
||||||
height={160} // gambar fix height
|
height={160}
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
radius="sm"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Card.Section>
|
</Card.Section>
|
||||||
<Stack gap="xs" mt="sm">
|
<Stack gap={4} mt="sm">
|
||||||
<Text fw={600} lineClamp={2}>
|
<Text ta="justify"
|
||||||
{item.judul}
|
size="sm"
|
||||||
</Text>
|
c="dimmed"
|
||||||
<Text size="sm" color="dimmed" lineClamp={2}>
|
lineClamp={3}
|
||||||
{item.deskripsi}
|
style={{
|
||||||
</Text>
|
wordBreak: "break-word",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "normal",
|
||||||
|
lineHeight: 1.5,
|
||||||
|
display: "-webkit-box",
|
||||||
|
WebkitLineClamp: 3,
|
||||||
|
WebkitBoxOrient: "vertical",
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.deskripsi || '-' }} />
|
||||||
<Text size="xs" c="gray">
|
<Text size="xs" c="gray">
|
||||||
{dayjs(item.createdAt).fromNow()}
|
{dayjs(item.createdAt).fromNow()}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
|
</motion.div>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</Stack>
|
</Stack>
|
||||||
<Divider color={colors['blue-button']} my="md" />
|
|
||||||
<Grid>
|
<Divider color={colors['blue-button']} my="lg" />
|
||||||
<GridCol span={{ md: 6, base: 12 }}>
|
|
||||||
|
{/* === PENGUMUMAN === */}
|
||||||
|
<Grid gutter="xl" align="stretch">
|
||||||
|
<GridCol span={{ base: 12, md: 6 }}>
|
||||||
{dataPengumuman && (
|
{dataPengumuman && (
|
||||||
<Paper h={"97%"} shadow="md" radius="md" p="md">
|
<motion.div
|
||||||
<Stack gap={"xs"}>
|
onClick={() => router.push(`/darmasaba/inovasi/layanan-online-desa/informasi-desa/detail-pengumuman/${dataPengumuman.id}`)}
|
||||||
<Title order={1} fw="bold">{dataPengumuman.judul}</Title>
|
whileHover={{ scale: 1.05 }}
|
||||||
<Text fz="sm" c="dimmed">{dataPengumuman.CategoryPengumuman?.name} • {dayjs(dataPengumuman.createdAt).fromNow()}</Text>
|
whileTap={{ scale: 0.95 }}
|
||||||
<Box>
|
style={{ cursor: "pointer" }}
|
||||||
<Text ta={"justify"} mt="xs" fz="md" style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: dataPengumuman.content }} />
|
>
|
||||||
</Box>
|
<Paper shadow="md" radius="lg" p="lg" h="100%" withBorder>
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Title order={2} fw={800}>
|
||||||
|
{dataPengumuman.judul}
|
||||||
|
</Title>
|
||||||
|
<Group justify='space-between'>
|
||||||
|
<Badge bg={colors['blue-button']}>
|
||||||
|
{dataPengumuman.CategoryPengumuman?.name}
|
||||||
|
</Badge>
|
||||||
|
<Text fz="sm" c="dimmed">
|
||||||
|
{dayjs(dataPengumuman.createdAt).fromNow()}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Text
|
||||||
|
ta="justify"
|
||||||
|
mt="xs"
|
||||||
|
fz="md"
|
||||||
|
lh={1.7}
|
||||||
|
lineClamp={8}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: dataPengumuman.content }}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</GridCol>
|
</GridCol>
|
||||||
<GridCol span={{ md: 6, base: 12 }}>
|
|
||||||
<Stack py={10}>
|
<GridCol span={{ base: 12, md: 6 }}>
|
||||||
<Title order={3}>Pengumuman Terbaru</Title>
|
<Stack>
|
||||||
<Grid>
|
<Title order={3} fw={700}>
|
||||||
|
Pengumuman Terbaru
|
||||||
|
</Title>
|
||||||
|
<Grid gutter="lg">
|
||||||
{statePengumuman.findRecent.data.map((item) => (
|
{statePengumuman.findRecent.data.map((item) => (
|
||||||
<GridCol span={{ base: 12, sm: 8, md: 6 }} key={item.id}>
|
<GridCol span={{ base: 12, sm: 6 }} key={item.id}>
|
||||||
<Card shadow="sm" radius="md" withBorder h="100%">
|
<motion.div
|
||||||
<Stack gap="xs" mt="sm">
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onClick={() => router.push(`/darmasaba/inovasi/layanan-online-desa/informasi-desa/detail-pengumuman/${item.id}`)}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
shadow="xs"
|
||||||
|
radius="md"
|
||||||
|
withBorder
|
||||||
|
h="100%"
|
||||||
|
p="md"
|
||||||
|
style={{ transition: '0.2s ease' }}
|
||||||
|
className="hover:shadow-md"
|
||||||
|
>
|
||||||
|
<Stack gap="xs">
|
||||||
<Text fw={600} lineClamp={2}>
|
<Text fw={600} lineClamp={2}>
|
||||||
{item.judul}
|
{item.judul}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" color="dimmed" lineClamp={2}>
|
<Text
|
||||||
{item.deskripsi}
|
ta="justify"
|
||||||
</Text>
|
mt="xs"
|
||||||
|
fz="md"
|
||||||
|
lh={1.7}
|
||||||
|
lineClamp={2}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: item.content }}
|
||||||
|
/>
|
||||||
<Text size="xs" c="gray">
|
<Text size="xs" c="gray">
|
||||||
{dayjs(item.createdAt).fromNow()}
|
{dayjs(item.createdAt).fromNow()}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
|
</motion.div>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -100,33 +100,33 @@ function PengaduanMasyarakat() {
|
|||||||
<Title order={3}>Ajukan Pengaduan Masyarakat</Title>
|
<Title order={3}>Ajukan Pengaduan Masyarakat</Title>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fz="sm" fw="bold">Nama</Text>}
|
label={<Text fz="sm" fw="bold">Nama</Text>}
|
||||||
placeholder="masukkan nama"
|
placeholder="Masukkan nama"
|
||||||
onChange={(val) => (state.pengaduanMasyarakat.create.form.name = val.target.value)}
|
onChange={(val) => (state.pengaduanMasyarakat.create.form.name = val.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fz="sm" fw="bold">Email</Text>}
|
label={<Text fz="sm" fw="bold">Email</Text>}
|
||||||
placeholder="masukkan email"
|
placeholder="Masukkan email"
|
||||||
onChange={(val) => (state.pengaduanMasyarakat.create.form.email = val.target.value)}
|
onChange={(val) => (state.pengaduanMasyarakat.create.form.email = val.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
type="number"
|
type="number"
|
||||||
label={<Text fz="sm" fw="bold">Nomor Telepon</Text>}
|
label={<Text fz="sm" fw="bold">Nomor Telepon</Text>}
|
||||||
placeholder="masukkan nomor telepon"
|
placeholder="Masukkan nomor telepon"
|
||||||
onChange={(val) => (state.pengaduanMasyarakat.create.form.nomorTelepon = val.target.value)}
|
onChange={(val) => (state.pengaduanMasyarakat.create.form.nomorTelepon = val.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fz="sm" fw="bold">NIK</Text>}
|
label={<Text fz="sm" fw="bold">NIK</Text>}
|
||||||
placeholder="masukkan nik"
|
placeholder="Masukkan nik"
|
||||||
onChange={(val) => (state.pengaduanMasyarakat.create.form.nik = val.target.value)}
|
onChange={(val) => (state.pengaduanMasyarakat.create.form.nik = val.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fz="sm" fw="bold">Judul Pengaduan</Text>}
|
label={<Text fz="sm" fw="bold">Judul Pengaduan</Text>}
|
||||||
placeholder="masukkan judul pengaduan"
|
placeholder="Masukkan judul pengaduan"
|
||||||
onChange={(val) => (state.pengaduanMasyarakat.create.form.judulPengaduan = val.target.value)}
|
onChange={(val) => (state.pengaduanMasyarakat.create.form.judulPengaduan = val.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fz="sm" fw="bold">Lokasi Kejadian</Text>}
|
label={<Text fz="sm" fw="bold">Lokasi Kejadian</Text>}
|
||||||
placeholder="masukkan lokasi kejadian"
|
placeholder="Masukkan lokasi kejadian"
|
||||||
onChange={(val) => (state.pengaduanMasyarakat.create.form.lokasiKejadian = val.target.value)}
|
onChange={(val) => (state.pengaduanMasyarakat.create.form.lokasiKejadian = val.target.value)}
|
||||||
/>
|
/>
|
||||||
<Box>
|
<Box>
|
||||||
@@ -144,7 +144,7 @@ function PengaduanMasyarakat() {
|
|||||||
state.pengaduanMasyarakat.create.form.jenisPengaduanId = val ?? "";
|
state.pengaduanMasyarakat.create.form.jenisPengaduanId = val ?? "";
|
||||||
}}
|
}}
|
||||||
label={<Text fw={"bold"} fz={"sm"}>Jenis Pengaduan</Text>}
|
label={<Text fw={"bold"} fz={"sm"}>Jenis Pengaduan</Text>}
|
||||||
placeholder="Pilih kategori produk"
|
placeholder="Pilih jenis pengaduan"
|
||||||
data={
|
data={
|
||||||
state.jenisPengaduan.findMany.data?.map((v) => ({
|
state.jenisPengaduan.findMany.data?.map((v) => ({
|
||||||
value: v.id,
|
value: v.id,
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
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 keamananLingkunganState from '@/app/admin/(dashboard)/_state/keamanan/keamanan-lingkungan'
|
||||||
|
import colors from '@/con/colors'
|
||||||
|
import BackButton from '../../../desa/layanan/_com/BackButto'
|
||||||
|
|
||||||
|
function DetailKeamananLingkunganUser() {
|
||||||
|
const keamananState = useProxy(keamananLingkunganState)
|
||||||
|
const params = useParams()
|
||||||
|
|
||||||
|
// Ambil data berdasarkan ID dari URL
|
||||||
|
useShallowEffect(() => {
|
||||||
|
keamananState.findUnique.load(params?.id as string)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Loading state
|
||||||
|
if (!keamananState.findUnique.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={40}>
|
||||||
|
<Skeleton height={500} radius="md" />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = keamananState.findUnique.data
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap="lg">
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<BackButton />
|
||||||
|
|
||||||
|
{/* Wrapper Detail */}
|
||||||
|
<Paper
|
||||||
|
withBorder
|
||||||
|
w={{ base: '100%', md: '80%' }}
|
||||||
|
mx="auto"
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="xl"
|
||||||
|
radius="lg"
|
||||||
|
shadow="md"
|
||||||
|
>
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Judul */}
|
||||||
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: 'xl', md: '2xl' }}
|
||||||
|
fw={700}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
>
|
||||||
|
{data?.name || 'Tanpa Judul'}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{/* Gambar */}
|
||||||
|
<Center>
|
||||||
|
<Image
|
||||||
|
w={{ base: 250, sm: 400, md: 550 }}
|
||||||
|
src={data?.image?.link}
|
||||||
|
alt={data?.name || 'gambar keamanan lingkungan'}
|
||||||
|
radius="md"
|
||||||
|
loading="lazy"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
|
||||||
|
{/* Deskripsi */}
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold" mb={5}>
|
||||||
|
Deskripsi
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
fz="md"
|
||||||
|
c="dimmed"
|
||||||
|
dangerouslySetInnerHTML={{ __html: data?.deskripsi || '-' }}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailKeamananLingkunganUser
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import keamananLingkunganState from '@/app/admin/(dashboard)/_state/keamanan/keamanan-lingkungan';
|
import keamananLingkunganState from '@/app/admin/(dashboard)/_state/keamanan/keamanan-lingkungan';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Spoiler, Stack, Text, TextInput } from '@mantine/core';
|
import { Box, Button, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
|
||||||
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconSearch } from '@tabler/icons-react';
|
import { IconSearch } from '@tabler/icons-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
const state = useProxy(keamananLingkunganState)
|
const state = useProxy(keamananLingkunganState)
|
||||||
const [expandedMap, setExpandedMap] = useState<Record<number, boolean>>({});
|
const router = useRouter()
|
||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
|
const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
|
||||||
const {
|
const {
|
||||||
@@ -26,13 +27,6 @@ function Page() {
|
|||||||
load(page, 3, debouncedSearch)
|
load(page, 3, debouncedSearch)
|
||||||
}, [page, debouncedSearch])
|
}, [page, debouncedSearch])
|
||||||
|
|
||||||
const toggleExpanded = (index: number, value: boolean) => {
|
|
||||||
setExpandedMap((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[index]: value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
@@ -64,61 +58,101 @@ function Page() {
|
|||||||
/>
|
/>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz={{ base: "h4", md: "h3" }} >
|
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz="md" mt={4} >
|
||||||
Keamanan dan ketertiban lingkungan di Desa Darmasaba dijaga melalui peran aktif Pecalang dan Patwal (Patroli Pengawal). Mereka bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga.
|
Pecalang dan Patwal (Patroli Pengawal) bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
<Box px={{ base: "md", md: 100 }}>
|
||||||
<Stack gap={'lg'}>
|
<Stack gap={'lg'}>
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
pb={10}
|
cols={{ base: 1, sm: 2, md: 3 }} spacing="xl" mt="lg">
|
||||||
cols={{
|
{data.map((v, k) => (
|
||||||
base: 1,
|
<Paper
|
||||||
md: 3,
|
key={k}
|
||||||
}}>
|
radius="xl"
|
||||||
{data.map((v, k) => {
|
shadow="md"
|
||||||
return (
|
withBorder
|
||||||
<Paper radius={10} key={k} bg={colors["white-trans-1"]}>
|
p="lg"
|
||||||
<Stack gap={'xs'}>
|
bg={colors['white-trans-1']}
|
||||||
<Center px={10} py={20}>
|
style={{
|
||||||
<Image loading="lazy" src={v.image?.link} alt='' />
|
transition: 'all 200ms ease',
|
||||||
</Center>
|
cursor: 'pointer',
|
||||||
<Box px={'lg'}>
|
display: 'flex',
|
||||||
<Box pb={20}>
|
flexDirection: 'column',
|
||||||
<Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}>
|
justifyContent: 'space-between',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack align="center" gap="sm" style={{ flexGrow: 1 }}>
|
||||||
|
<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 ta="center" fw={700} fz="lg" c={colors['blue-button']}>
|
||||||
{v.name}
|
{v.name}
|
||||||
</Text>
|
</Text>
|
||||||
<Spoiler
|
<Text
|
||||||
showLabel={
|
fz="sm"
|
||||||
<Text fw="bold" fz="sm" c={colors['blue-button']}>
|
ta="justify"
|
||||||
Show more
|
lineClamp={3}
|
||||||
</Text>
|
lh={1.6}
|
||||||
}
|
style={{
|
||||||
hideLabel={
|
minHeight: '4.8em',
|
||||||
<Text fw="bold" fz="sm" c={colors['blue-button']}>
|
}}
|
||||||
Hide details
|
|
||||||
</Text>
|
|
||||||
}
|
|
||||||
expanded={expandedMap[k] || false}
|
|
||||||
onExpandedChange={(val) => toggleExpanded(k, val)}
|
|
||||||
>
|
>
|
||||||
<Text pb={10} fz={"h4"} ta={'justify'} style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
|
<span
|
||||||
</Spoiler>
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
</Box>
|
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
||||||
</Box>
|
/>
|
||||||
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Center mt="md">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
onClick={() => {
|
||||||
|
router.push(`/darmasaba/keamanan/keamanan-lingkungan-pecalang-patwal/${v.id}`)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Button>
|
||||||
|
</Center>
|
||||||
</Paper>
|
</Paper>
|
||||||
)
|
))}
|
||||||
})}
|
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
<Center>
|
|
||||||
|
<Center mt="xl">
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
onChange={(newPage) => load(newPage)} // ini penting!
|
onChange={(newPage) => load(newPage, 3, search)}
|
||||||
total={totalPages}
|
total={totalPages}
|
||||||
my="md"
|
size="lg"
|
||||||
|
radius="xl"
|
||||||
|
styles={{
|
||||||
|
control: {
|
||||||
|
border: `1px solid ${colors['blue-button']}`,
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ function Page() {
|
|||||||
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
||||||
Kontak Darurat
|
Kontak Darurat
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz={{ base: "h4", md: "h3" }} >
|
<Text fz="md" >
|
||||||
Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung.
|
Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||||
import laporanPublikState from '@/app/admin/(dashboard)/_state/keamanan/laporan-publik';
|
import laporanPublikState from '@/app/admin/(dashboard)/_state/keamanan/laporan-publik';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Center, ColorSwatch, Flex, Group, Modal, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
|
import { Box, Button, Center, ColorSwatch, Flex, Group, Modal, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
|
||||||
import { DateTimePicker } from '@mantine/dates';
|
import { DateTimePicker } from '@mantine/dates';
|
||||||
import { useDebouncedValue, useDisclosure, useShallowEffect } from '@mantine/hooks';
|
import { useDebouncedValue, useDisclosure, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconArrowRight, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconArrowRight, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useTransitionRouter } from 'next-view-transitions';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
import { useTransitionRouter } from 'next-view-transitions';
|
|
||||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
@@ -53,7 +53,7 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Flex justify="space-between" align="center">
|
<Group justify="space-between" align="center">
|
||||||
<BackButton />
|
<BackButton />
|
||||||
<TextInput
|
<TextInput
|
||||||
radius={"lg"}
|
radius={"lg"}
|
||||||
@@ -61,9 +61,9 @@ function Page() {
|
|||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
leftSection={<IconSearch size={20} />}
|
leftSection={<IconSearch size={20} />}
|
||||||
w={{ base: "50%", md: "100%" }}
|
w={{ base: "100%", md: "30%" }}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Group>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Group justify="space-between">
|
<Group justify="space-between">
|
||||||
@@ -118,7 +118,7 @@ function Page() {
|
|||||||
return (
|
return (
|
||||||
<Paper radius={'lg'} key={k} bg={colors['white-trans-1']} p={'xl'}>
|
<Paper radius={'lg'} key={k} bg={colors['white-trans-1']} p={'xl'}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title c={colors['blue-button']} order={1}>{v.judul}</Title>
|
<Text c={colors['blue-button']} lineClamp={3} truncate="end" fz="h4" fw="bold">{v.judul}</Text>
|
||||||
<Text fs={'italic'} fz={'xl'}>
|
<Text fs={'italic'} fz={'xl'}>
|
||||||
{v.tanggalWaktu
|
{v.tanggalWaktu
|
||||||
? new Date(v.tanggalWaktu).toLocaleString('id-ID')
|
? new Date(v.tanggalWaktu).toLocaleString('id-ID')
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ function DetailPencegahanKriminalitas() {
|
|||||||
const data = kriminalitasState.findUnique.data;
|
const data = kriminalitasState.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py="md" px="md">
|
<Box py="md" px={{ base: 'md', md: 100 }}>
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ function Page() {
|
|||||||
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
||||||
Pencegahan Kriminalitas
|
Pencegahan Kriminalitas
|
||||||
</Text>
|
</Text>
|
||||||
<Text c={colors['blue-button']} fz={{ base: 'h4', md: 'h3' }}>
|
<Text fz='md'>
|
||||||
Keamanan Komunitas & Pencegahan Kriminal
|
Keamanan Komunitas & Pencegahan Kriminal
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -78,7 +78,7 @@ function Page() {
|
|||||||
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
||||||
Pencegahan Kriminalitas
|
Pencegahan Kriminalitas
|
||||||
</Text>
|
</Text>
|
||||||
<Text c={colors['blue-button']} fz={{ base: 'h4', md: 'h3' }}>
|
<Text fz='md'>
|
||||||
Keamanan Komunitas & Pencegahan Kriminal
|
Keamanan Komunitas & Pencegahan Kriminal
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -92,31 +92,63 @@ function Page() {
|
|||||||
Program Keamanan Berjalan
|
Program Keamanan Berjalan
|
||||||
</Text>
|
</Text>
|
||||||
<Stack pt={30} gap="lg">
|
<Stack pt={30} gap="lg">
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
minHeight: 300, // sesuaikan: tinggi area yg muat 3 item
|
||||||
|
}}
|
||||||
|
>
|
||||||
{data.length > 0 ? (
|
{data.length > 0 ? (
|
||||||
data.map((item) => (
|
data.map((item) => (
|
||||||
<a key={item.id} href={`/darmasaba/keamanan/pencegahan-kriminalitas/${item.id}`}>
|
<Paper
|
||||||
<Paper p="md" bg={colors['blue-button']} radius="md" shadow="sm">
|
key={item.id}
|
||||||
<Stack gap={"xs"}>
|
p="md"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
backgroundColor: colors['blue-button'],
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
}}
|
||||||
|
onClick={() =>
|
||||||
|
router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/${item.id}`)
|
||||||
|
}
|
||||||
|
onMouseEnter={(e) =>
|
||||||
|
(e.currentTarget.style.backgroundColor = '#1a3e7a')
|
||||||
|
}
|
||||||
|
onMouseLeave={(e) =>
|
||||||
|
(e.currentTarget.style.backgroundColor = colors['blue-button'])
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Stack gap="xs">
|
||||||
<Text fz="h3" c={colors['white-1']}>
|
<Text fz="h3" c={colors['white-1']}>
|
||||||
{item.judul}
|
{item.judul}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</a>
|
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<Text color="dimmed">
|
<Text c="dimmed">Tidak ada data pencegahan kriminalitas yang cocok</Text>
|
||||||
Tidak ada data pencegahan kriminalitas yang cocok
|
|
||||||
</Text>
|
|
||||||
)}
|
)}
|
||||||
|
</Box>
|
||||||
<Button
|
<Button
|
||||||
mt={20}
|
mt={20}
|
||||||
fullWidth
|
fullWidth
|
||||||
radius="xl"
|
radius="xl"
|
||||||
size="md"
|
size="md"
|
||||||
bg={colors['blue-button']}
|
variant="outline"
|
||||||
rightSection={<IconArrowRight size={20} color={colors['white-1']} />}
|
color="blue"
|
||||||
onClick={() => router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/program-lainnya`)}
|
rightSection={<IconArrowRight size={20} />}
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
fontWeight: 600,
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/darmasaba/keamanan/pencegahan-kriminalitas/program-lainnya`
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Jelajahi Program Lainnya
|
Jelajahi Program Lainnya
|
||||||
</Button>
|
</Button>
|
||||||
@@ -142,9 +174,7 @@ function Page() {
|
|||||||
<Text py={10} fz={{ base: 'h3', md: 'h2' }} fw="bold" c={colors['blue-button']}>
|
<Text py={10} fz={{ base: 'h3', md: 'h2' }} fw="bold" c={colors['blue-button']}>
|
||||||
{findFirst.data?.judul}
|
{findFirst.data?.judul}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="h4" c={colors['blue-button']}>
|
<Text fz="h4" dangerouslySetInnerHTML={{ __html: findFirst.data?.deskripsiSingkat }} />
|
||||||
{findFirst.data?.deskripsiSingkat}
|
|
||||||
</Text>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
) : null}
|
) : null}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -21,12 +21,23 @@ import pencegahanKriminalitasState from '@/app/admin/(dashboard)/_state/keamanan
|
|||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
|
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
|
||||||
|
import { IconArrowLeft } from '@tabler/icons-react';
|
||||||
|
|
||||||
function PencegahanKriminalitas() {
|
function PencegahanKriminalitas() {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
const router = useRouter();
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box pt={20} px={{ base: 'md', md: 100 }}>
|
||||||
|
<Group mb="md">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
leftSection={<IconArrowLeft size={20} />}
|
||||||
|
>
|
||||||
|
Kembali
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
<HeaderSearch
|
<HeaderSearch
|
||||||
title="Program Pencegahan Kriminalitas"
|
title="Program Pencegahan Kriminalitas"
|
||||||
placeholder="Cari program atau deskripsi..."
|
placeholder="Cari program atau deskripsi..."
|
||||||
|
|||||||
@@ -9,21 +9,16 @@ import { useProxy } from 'valtio/utils';
|
|||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
const state = useProxy(polsekTerdekatState.findFirst);
|
const state = useProxy(polsekTerdekatState.findFirst);
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
const {
|
const { data, loading, load } = state;
|
||||||
data,
|
|
||||||
loading,
|
|
||||||
load,
|
|
||||||
} = state;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load();
|
load();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// kalau masih loading
|
// Loading state
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
@@ -32,18 +27,18 @@ function Page() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// kalau data kosong
|
// Data kosong
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return (
|
return (
|
||||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
</Box>
|
||||||
<Box pb={10} px={{ base: 20, md: 100 }}>
|
<Box pb={10} px={{ base: 20, md: 100 }}>
|
||||||
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
||||||
Kantor Polisi Terdekat
|
Kantor Polisi Terdekat
|
||||||
</Text>
|
</Text>
|
||||||
<Text pb={15} fz={'h4'} >
|
<Text pb={15} fz="md">
|
||||||
Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung
|
Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -57,79 +52,150 @@ function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box pb={10} px={{ base: 20, md: 100 }}>
|
<Box pb={10} px={{ base: 20, md: 100 }}>
|
||||||
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
|
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
||||||
Kantor Polisi Terdekat
|
Kantor Polisi Terdekat
|
||||||
</Text>
|
</Text>
|
||||||
<Text pb={15} fz={'h4'} >
|
<Text pb={15} fz="h4">
|
||||||
Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung
|
Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
|
||||||
<Stack gap={'lg'}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Paper radius={10} bg={colors["white-trans-1"]} p={'xl'}>
|
<Stack gap="lg">
|
||||||
<Stack gap={'xs'}>
|
<Paper radius={10} bg={colors['white-trans-1']} p="xl">
|
||||||
<SimpleGrid
|
<Stack gap="xs">
|
||||||
cols={{
|
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||||
base: 1,
|
|
||||||
md: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Content Sebelah Kiri */}
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Center><Skeleton h={400} /></Center>
|
<Center>
|
||||||
) : data ? (
|
<Skeleton h={400} />
|
||||||
|
</Center>
|
||||||
|
) : (
|
||||||
<>
|
<>
|
||||||
|
{/* === KIRI === */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text c={colors["blue-button"]} fw={'bold'} fz={'h2'}>{data.nama}</Text>
|
<Text c={colors['blue-button']} fw="bold" fz="h2">
|
||||||
<Text c={colors["blue-button"]} fz={'sm'}>{data.jarakKeDesa}</Text>
|
{data.nama}
|
||||||
<Flex py={10} gap={9} align={'center'}>
|
</Text>
|
||||||
<IconPin size={25} color={colors["blue-button"]} />
|
<Text c={colors['blue-button']} fz="sm">
|
||||||
<Text c={colors["blue-button"]} fz={'lg'}>{data.alamat}</Text>
|
{data.jarakKeDesa}
|
||||||
</Flex>
|
</Text>
|
||||||
<Flex gap={9} align={'center'}>
|
|
||||||
<IconPhone size={25} color={colors["blue-button"]} />
|
{/* Alamat */}
|
||||||
<Text c={colors["blue-button"]} fz={'lg'}>{data.nomorTelepon}</Text>
|
<Flex
|
||||||
</Flex>
|
|
||||||
<Flex py={10} gap={9} align={'center'}>
|
|
||||||
<IconClock size={25} color={colors["blue-button"]} />
|
|
||||||
<Text c={colors["blue-button"]} fz={'lg'}>{data.jamOperasional}</Text>
|
|
||||||
</Flex>
|
|
||||||
<Box>
|
|
||||||
<Text c={colors["blue-button"]} fw={'bold'} fz={'h2'}>Layanan Yang Tersedia :</Text>
|
|
||||||
<SimpleGrid
|
|
||||||
py={10}
|
py={10}
|
||||||
cols={{
|
gap={9}
|
||||||
base: 1,
|
align="flex-start"
|
||||||
md: 2,
|
wrap="wrap"
|
||||||
|
style={{ wordBreak: 'break-word' }}
|
||||||
|
>
|
||||||
|
<Box w={25} mt={3}>
|
||||||
|
<IconPin size={22} />
|
||||||
|
</Box>
|
||||||
|
<Text
|
||||||
|
fz="lg"
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
lineHeight: 1.4,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box>
|
{data.alamat}
|
||||||
<Text c={colors["blue-button"]} fz={'lg'}>{data.layananPolsek.nama}</Text>
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{/* Telepon */}
|
||||||
|
<Flex
|
||||||
|
gap={9}
|
||||||
|
align="flex-start"
|
||||||
|
wrap="wrap"
|
||||||
|
style={{ wordBreak: 'break-word' }}
|
||||||
|
>
|
||||||
|
<Box w={25} mt={3}>
|
||||||
|
<IconPhone size={22} />
|
||||||
</Box>
|
</Box>
|
||||||
|
<Text fz="lg">
|
||||||
|
{data.nomorTelepon}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{/* Jam Operasional */}
|
||||||
|
<Flex
|
||||||
|
py={10}
|
||||||
|
gap={9}
|
||||||
|
align="flex-start"
|
||||||
|
wrap="wrap"
|
||||||
|
style={{ wordBreak: 'break-word' }}
|
||||||
|
>
|
||||||
|
<Box w={25} mt={3}>
|
||||||
|
<IconClock size={22} />
|
||||||
|
</Box>
|
||||||
|
<Text fz="lg">
|
||||||
|
{data.jamOperasional}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{/* Layanan */}
|
||||||
|
<Box>
|
||||||
|
<Text c={colors['blue-button']} fw="bold" fz="h2">
|
||||||
|
Layanan Yang Tersedia :
|
||||||
|
</Text>
|
||||||
|
<SimpleGrid py={10} cols={{ base: 1, md: 2 }}>
|
||||||
|
<Text fz="lg">
|
||||||
|
{data.layananPolsek.nama}
|
||||||
|
</Text>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Button bg={colors["blue-button"]} onClick={() => router.push(`/darmasaba/keamanan/polsek-terdekat/semua-polsek`)} rightSection={<IconArrowDown size={20} />}>Lihat Kantor Polisi Lainnya</Button>
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
onClick={() =>
|
||||||
|
router.push(`/darmasaba/keamanan/polsek-terdekat/semua-polsek`)
|
||||||
|
}
|
||||||
|
rightSection={<IconArrowDown size={20} />}
|
||||||
|
>
|
||||||
|
Lihat Kantor Polisi Lainnya
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box pos={'relative'}>
|
|
||||||
|
{/* === KANAN === */}
|
||||||
|
<Box pos="relative">
|
||||||
<Box style={{ position: 'absolute', top: 0, right: 0 }}>
|
<Box style={{ position: 'absolute', top: 0, right: 0 }}>
|
||||||
<Badge size='lg' c={'#287407'} bg={'#A8EDC4'}>Buka</Badge>
|
<Badge size="lg" c="#287407" bg="#A8EDC4">
|
||||||
|
Buka
|
||||||
|
</Badge>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box pt={40}>
|
<Box pt={40}>
|
||||||
<iframe style={{ border: 2, width: "100%" }} src={data.embedMapUrl} width="550" height="300" ></iframe>
|
<iframe
|
||||||
|
style={{ border: 2, width: '100%' }}
|
||||||
|
src={data.embedMapUrl}
|
||||||
|
width="550"
|
||||||
|
height="300"
|
||||||
|
></iframe>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box pt={20}>
|
<Box pt={20}>
|
||||||
<Button onClick={() => router.push(data.linkPetunjukArah)} fullWidth bg={colors["blue-button"]} radius={10} leftSection={<IconNavigation size={20} />}>Petunjuk Arah</Button>
|
<Button
|
||||||
|
onClick={() => router.push(data.linkPetunjukArah)}
|
||||||
|
fullWidth
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
radius={10}
|
||||||
|
leftSection={<IconNavigation size={20} />}
|
||||||
|
>
|
||||||
|
Petunjuk Arah
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
) : null}
|
)}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -56,8 +56,11 @@ function Page() {
|
|||||||
/>
|
/>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz={{ base: "h4", md: "h3" }} >
|
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz="md" >
|
||||||
Keamanan dan ketertiban lingkungan di Desa Darmasaba dijaga melalui peran aktif Pecalang dan Patwal (Patroli Pengawal). Mereka bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga.
|
Keamanan dan ketertiban lingkungan di Desa Darmasaba dijaga melalui peran aktif Pecalang dan Patwal (Patroli Pengawal).
|
||||||
|
</Text>
|
||||||
|
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz="md" >
|
||||||
|
Mereka bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
<Box px={{ base: "md", md: 100 }}>
|
||||||
|
|||||||
@@ -78,15 +78,12 @@ function Page() {
|
|||||||
<Box>
|
<Box>
|
||||||
<Text fz="h4" fw="bold">Pendahuluan</Text>
|
<Text fz="h4" fw="bold">Pendahuluan</Text>
|
||||||
<Divider my="xs" />
|
<Divider my="xs" />
|
||||||
<Text fz="md" lh={1.6} ta="justify">
|
<Text fz="md" lh={1.6} ta="justify" dangerouslySetInnerHTML={{ __html: state.findUnique.data.introduction?.content }} />
|
||||||
{state.findUnique.data.introduction?.content}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="h4" fw="bold">Kenali Gejala DBD</Text>
|
<Text fz="h4" fw="bold">{state.findUnique.data.symptom?.title}</Text>
|
||||||
<Divider my="xs" />
|
<Divider my="xs" />
|
||||||
<Text fz="md" fw="semibold">{state.findUnique.data.symptom?.title}</Text>
|
|
||||||
<Text fz="md" lh={1.6} ta="justify" style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: state.findUnique.data.symptom?.content }} />
|
<Text fz="md" lh={1.6} ta="justify" style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: state.findUnique.data.symptom?.content }} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Anchor, Box, Card, Divider, Group, Image, Loader, Paper, Stack, Text, Title, Tooltip } from '@mantine/core';
|
import { Box, Button, Card, Divider, Group, Image, Loader, Paper, Stack, Text } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconCalendar, IconChevronRight } from '@tabler/icons-react';
|
import { IconCalendar, IconChevronRight } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -28,9 +28,9 @@ function ArtikelKesehatanPage() {
|
|||||||
<Box>
|
<Box>
|
||||||
<Paper p="xl" bg={colors['white-trans-1']} radius="xl" shadow="md">
|
<Paper p="xl" bg={colors['white-trans-1']} radius="xl" shadow="md">
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Title order={2} ta="center" c={colors['blue-button']}>
|
<Text ta="center" fw={700} fz="32px" c={colors['blue-button']}>
|
||||||
Artikel Kesehatan
|
Artikel Kesehatan
|
||||||
</Title>
|
</Text>
|
||||||
<Divider size="sm" color={colors['blue-button']} />
|
<Divider size="sm" color={colors['blue-button']} />
|
||||||
{state.findMany.data.length === 0 ? (
|
{state.findMany.data.length === 0 ? (
|
||||||
<Box py="xl" ta="center">
|
<Box py="xl" ta="center">
|
||||||
@@ -51,31 +51,30 @@ function ArtikelKesehatanPage() {
|
|||||||
onMouseLeave={(e) => (e.currentTarget.style.transform = 'translateY(0)')}
|
onMouseLeave={(e) => (e.currentTarget.style.transform = 'translateY(0)')}
|
||||||
>
|
>
|
||||||
<Card.Section>
|
<Card.Section>
|
||||||
<Image src={item.image?.link} alt={item.title} height={200} fit="cover" loading="lazy"/>
|
<Image style={{ borderTopLeftRadius: '10px', borderTopRightRadius: '10px' }} src={item.image?.link} alt={item.title} height={200} fit="cover" loading="lazy" />
|
||||||
</Card.Section>
|
</Card.Section>
|
||||||
<Stack gap="xs" mt="md">
|
<Stack gap="xs" mt="md">
|
||||||
<Text fw="bold" fz="xl" c="dark">{item.title}</Text>
|
<Text fw="bold" fz="xl" c={colors['blue-button']}>{item.title}</Text>
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<IconCalendar size={16} color={colors['blue-button']} />
|
<IconCalendar size={16} color='gray' />
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
{new Date(item.createdAt).toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' })} • Dinas Kesehatan
|
{new Date(item.createdAt).toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' })} • Dinas Kesehatan
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Text fz="md" c="dark" lineClamp={3}>
|
<Text fz="md" lineClamp={3}>
|
||||||
{item.content}
|
{item.content}
|
||||||
</Text>
|
</Text>
|
||||||
<Tooltip label="Baca artikel lengkap">
|
<Group justify="flex-start">
|
||||||
<Anchor
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
radius="lg"
|
||||||
|
size="sm"
|
||||||
|
rightSection={<IconChevronRight size={18} />}
|
||||||
onClick={() => router.push(`/darmasaba/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/${item.id}`)}
|
onClick={() => router.push(`/darmasaba/kesehatan/data-kesehatan-warga/artikel-kesehatan-page/${item.id}`)}
|
||||||
variant="light"
|
|
||||||
c={colors['blue-button']}
|
|
||||||
>
|
>
|
||||||
<Group gap="xs">
|
Baca Selengkapnya
|
||||||
<Text fw="bold" fz="md">Baca Selengkapnya</Text>
|
</Button>
|
||||||
<IconChevronRight size={18} />
|
|
||||||
</Group>
|
</Group>
|
||||||
</Anchor>
|
|
||||||
</Tooltip>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Anchor, Badge, Box, Card, Divider, Group, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
import { Badge, Box, Button, Card, Divider, Group, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconChevronRight, IconClock, IconMapPin } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import { IconMapPin, IconClock, IconArrowRight } from '@tabler/icons-react';
|
|
||||||
|
|
||||||
function FasilitasKesehatanPage() {
|
function FasilitasKesehatanPage() {
|
||||||
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
||||||
@@ -73,31 +73,32 @@ function FasilitasKesehatanPage() {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<IconMapPin size={18} stroke={1.5} color={colors['blue-button']} />
|
<IconMapPin size={18} stroke={1.5} />
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm">
|
||||||
{item.informasiumum.alamat}
|
{item.informasiumum.alamat}
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<IconClock size={18} stroke={1.5} color={colors['blue-button']} />
|
<IconClock size={18} stroke={1.5} />
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm">
|
||||||
{item.informasiumum.jamOperasional}
|
{item.informasiumum.jamOperasional}
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Anchor
|
<Group justify="flex-start">
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
radius="lg"
|
||||||
|
size="sm"
|
||||||
|
rightSection={<IconChevronRight size={18} />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push(
|
router.push(
|
||||||
`/darmasaba/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/${item.id}`
|
`/darmasaba/kesehatan/data-kesehatan-warga/fasilitas-kesehatan-page/${item.id}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
c={colors['blue-button']}
|
|
||||||
fz="sm"
|
|
||||||
fw={600}
|
|
||||||
style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}
|
|
||||||
>
|
>
|
||||||
Lihat Detail
|
Lihat Detail
|
||||||
<IconArrowRight size={16} stroke={1.5} />
|
</Button>
|
||||||
</Anchor>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ function JadwalKegiatanPage() {
|
|||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Group justify="space-between">
|
<Group justify="space-between">
|
||||||
<Text fw={700} fz="xl">
|
<Text fw={700} fz="xl" c={colors['blue-button']}>
|
||||||
{item.informasijadwalkegiatan.name}
|
{item.informasijadwalkegiatan.name}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fw={600} fz="sm" c={colors['blue-button']}>
|
<Text fw={600} fz="sm" c={colors['blue-button']}>
|
||||||
@@ -62,20 +62,20 @@ function JadwalKegiatanPage() {
|
|||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<IconClockHour4 size={18} color={colors['blue-button']} />
|
<IconClockHour4 size={18} />
|
||||||
<Text fz="sm">{item.informasijadwalkegiatan.waktu}</Text>
|
<Text fz="sm">{item.informasijadwalkegiatan.waktu}</Text>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<IconMapPin size={18} color={colors['blue-button']} />
|
<IconMapPin size={18} />
|
||||||
<Text fz="sm">{item.informasijadwalkegiatan.lokasi}</Text>
|
<Text fz="sm">{item.informasijadwalkegiatan.lokasi}</Text>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Divider my="sm" />
|
<Divider my="sm" />
|
||||||
|
|
||||||
<Group justify="flex-end">
|
<Group justify="flex-start">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
bg={colors['blue-button']}
|
||||||
radius="lg"
|
radius="lg"
|
||||||
size="sm"
|
size="sm"
|
||||||
rightSection={<IconChevronRight size={18} />}
|
rightSection={<IconChevronRight size={18} />}
|
||||||
@@ -84,14 +84,6 @@ function JadwalKegiatanPage() {
|
|||||||
`/darmasaba/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/${item.id}`
|
`/darmasaba/kesehatan/data-kesehatan-warga/jadwal-kegiatan-page/${item.id}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
background: colors['blue-button'],
|
|
||||||
color: 'white',
|
|
||||||
boxShadow: '0 0 12px rgba(0, 123, 255, 0.4)',
|
|
||||||
transition: 'all 0.2s ease',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Lihat Detail & Daftar
|
Lihat Detail & Daftar
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ function DetailInfoWabahPenyakitUser() {
|
|||||||
const data = state.findUnique.data;
|
const data = state.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10} px={{ base: 'md', md: 100 }}>
|
||||||
{/* Tombol Back */}
|
{/* Tombol Back */}
|
||||||
|
<Box>
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
onClick={() => router.back()}
|
onClick={() => router.back()}
|
||||||
@@ -38,6 +39,7 @@ function DetailInfoWabahPenyakitUser() {
|
|||||||
>
|
>
|
||||||
Kembali
|
Kembali
|
||||||
</Button>
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* Wrapper Detail */}
|
{/* Wrapper Detail */}
|
||||||
<Paper
|
<Paper
|
||||||
@@ -71,7 +73,6 @@ function DetailInfoWabahPenyakitUser() {
|
|||||||
<Text fz="lg" fw="bold">Deskripsi</Text>
|
<Text fz="lg" fw="bold">Deskripsi</Text>
|
||||||
<Text
|
<Text
|
||||||
fz="md"
|
fz="md"
|
||||||
c="dimmed"
|
|
||||||
dangerouslySetInnerHTML={{ __html: data.deskripsiLengkap || '-' }}
|
dangerouslySetInnerHTML={{ __html: data.deskripsiLengkap || '-' }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Informasi Wabah & Penyakit
|
Informasi Wabah & Penyakit
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="md" c="dimmed" mt={4}>
|
<Text fz="md" mt={4}>
|
||||||
Dapatkan informasi terbaru mengenai wabah dan penyakit yang sedang
|
Dapatkan informasi terbaru mengenai wabah dan penyakit yang sedang
|
||||||
diawasi.
|
diawasi.
|
||||||
</Text>
|
</Text>
|
||||||
@@ -84,7 +84,7 @@ function Page() {
|
|||||||
<Center py="6xl">
|
<Center py="6xl">
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<IconInfoCircle size={50} color={colors['blue-button']} />
|
<IconInfoCircle size={50} color={colors['blue-button']} />
|
||||||
<Text fz="lg" fw={500} c="dimmed">
|
<Text fz="lg" fw={500} >
|
||||||
Tidak ada data yang cocok dengan pencarian Anda.
|
Tidak ada data yang cocok dengan pencarian Anda.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -101,17 +101,35 @@ function Page() {
|
|||||||
bg={colors['white-trans-1']}
|
bg={colors['white-trans-1']}
|
||||||
style={{
|
style={{
|
||||||
transition: 'transform 200ms ease, box-shadow 200ms ease',
|
transition: 'transform 200ms ease, box-shadow 200ms ease',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm" style={{ flex: 1 }}>
|
||||||
<Image
|
{/* Gambar */}
|
||||||
radius="md"
|
<Box
|
||||||
h={180}
|
h={180}
|
||||||
src={v.image.link}
|
w="100%"
|
||||||
|
style={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
borderRadius: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={v.image?.link}
|
||||||
alt={v.name}
|
alt={v.name}
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
w="100%"
|
||||||
|
h="100%"
|
||||||
|
style={{
|
||||||
|
objectFit: 'cover',
|
||||||
|
objectPosition: 'center',
|
||||||
|
}}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Judul dan badge */}
|
||||||
<Group justify="space-between" mt="sm">
|
<Group justify="space-between" mt="sm">
|
||||||
<Text fw={700} fz="lg" c={colors['blue-button']}>
|
<Text fw={700} fz="lg" c={colors['blue-button']}>
|
||||||
{v.name}
|
{v.name}
|
||||||
@@ -120,25 +138,46 @@ function Page() {
|
|||||||
Wabah
|
Wabah
|
||||||
</Badge>
|
</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
Diposting: {new Date(v.createdAt).toLocaleDateString('id-ID', {
|
Diposting:{' '}
|
||||||
|
{new Date(v.createdAt).toLocaleDateString('id-ID', {
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
})}
|
})}
|
||||||
|
</Text>
|
||||||
|
|
||||||
</Text>
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<Text fz="sm" lh={1.5} lineClamp={3} truncate="end">
|
|
||||||
{v.deskripsiSingkat}
|
{/* Bagian deskripsi dan tombol */}
|
||||||
</Text>
|
<Box style={{ display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
|
||||||
<Button variant="light" radius="md" size="md" onClick={() => router.push(`/darmasaba/kesehatan/info-wabah-penyakit/${v.id}`)}>
|
<Text
|
||||||
|
fz="sm"
|
||||||
|
lh={1.5}
|
||||||
|
lineClamp={3}
|
||||||
|
dangerouslySetInnerHTML={{ __html: v.deskripsiSingkat }}
|
||||||
|
style={{ flexGrow: 1 }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
mt="md"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(`/darmasaba/kesehatan/info-wabah-penyakit/${v.id}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
Selengkapnya
|
Selengkapnya
|
||||||
</Button>
|
</Button>
|
||||||
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ function Page() {
|
|||||||
<Text fz={{ base: '2rem', md: '2.8rem' }} c={colors['blue-button']} fw={800}>
|
<Text fz={{ base: '2rem', md: '2.8rem' }} c={colors['blue-button']} fw={800}>
|
||||||
Kontak Darurat
|
Kontak Darurat
|
||||||
</Text>
|
</Text>
|
||||||
<Text c="dimmed" fz="md" mt={4}>
|
<Text fz="md" mt={4}>
|
||||||
Hubungi layanan penting dengan cepat dan mudah
|
Hubungi layanan penting dengan cepat dan mudah
|
||||||
</Text>
|
</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
@@ -98,9 +98,13 @@ function Page() {
|
|||||||
style={{
|
style={{
|
||||||
transition: 'all 200ms ease',
|
transition: 'all 200ms ease',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'space-between', // ✅ biar button selalu di bawah
|
||||||
|
height: '100%', // ✅ bikin tinggi seragam
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm" style={{ flexGrow: 1 }}>
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@@ -128,9 +132,25 @@ function Page() {
|
|||||||
<Text ta="center" fw={700} fz="lg" c={colors['blue-button']}>
|
<Text ta="center" fw={700} fz="lg" c={colors['blue-button']}>
|
||||||
{v.name}
|
{v.name}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="sm" c="dimmed" ta="center" lineClamp={3}>
|
|
||||||
<span style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
|
<Text
|
||||||
|
fz="sm"
|
||||||
|
ta="center"
|
||||||
|
lineClamp={3}
|
||||||
|
lh={1.6}
|
||||||
|
style={{
|
||||||
|
minHeight: '4.8em', // tinggi tetap 3 baris
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
||||||
|
/>
|
||||||
</Text>
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{/* ✅ Tombol selalu di bagian bawah card */}
|
||||||
|
<Center mt="md">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
leftSection={<IconBrandWhatsapp size={18} />}
|
leftSection={<IconBrandWhatsapp size={18} />}
|
||||||
@@ -138,9 +158,13 @@ function Page() {
|
|||||||
href={`https://wa.me/${v.whatsapp.replace(/\D/g, '')}`}
|
href={`https://wa.me/${v.whatsapp.replace(/\D/g, '')}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
aria-label="Hubungi WhatsApp"
|
aria-label="Hubungi WhatsApp"
|
||||||
>WhatsApp</Button>
|
>
|
||||||
</Stack>
|
WhatsApp
|
||||||
|
</Button>
|
||||||
|
</Center>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
|
||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
'use client';
|
||||||
|
import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, 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 DetailPenangananDaruratUser() {
|
||||||
|
const state = useProxy(penangananDarurat);
|
||||||
|
const params = useParams();
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
state.findUnique.load(params?.id as string);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!state.findUnique.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={40}>
|
||||||
|
<Skeleton height={400} radius="md" />
|
||||||
|
<Skeleton height={20} width="80%" radius="md" />
|
||||||
|
<Skeleton height={20} width="60%" radius="md" />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = state.findUnique.data;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box py={20}>
|
||||||
|
{/* Tombol Back */}
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<BackButton/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Wrapper Detail */}
|
||||||
|
<Paper
|
||||||
|
withBorder
|
||||||
|
w={{ base: '100%', md: '70%', lg: '60%' }}
|
||||||
|
mx="auto"
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="xl"
|
||||||
|
radius="lg"
|
||||||
|
shadow="sm"
|
||||||
|
>
|
||||||
|
<Stack gap="md" align="center" ta="center">
|
||||||
|
<Text fz="xl" fw={700} c={colors['blue-button']}>
|
||||||
|
{data.name || 'Penanganan Darurat'}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{data.image?.link && (
|
||||||
|
<Image
|
||||||
|
src={data.image.link}
|
||||||
|
alt={data.name}
|
||||||
|
radius="md"
|
||||||
|
mah={300}
|
||||||
|
fit="contain"
|
||||||
|
loading="lazy"
|
||||||
|
mb="md"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text
|
||||||
|
fz="md"
|
||||||
|
ta="justify"
|
||||||
|
dangerouslySetInnerHTML={{ __html: data.deskripsi }}
|
||||||
|
style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailPenangananDaruratUser;
|
||||||
@@ -3,6 +3,7 @@ import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penangan
|
|||||||
import colors from '@/con/colors'
|
import colors from '@/con/colors'
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
Button,
|
||||||
Center,
|
Center,
|
||||||
Grid,
|
Grid,
|
||||||
GridCol,
|
GridCol,
|
||||||
@@ -51,7 +52,7 @@ function Page() {
|
|||||||
<Text fz={{ base: 30, md: 40 }} c={colors['blue-button']} fw={800} lh={1.2}>
|
<Text fz={{ base: 30, md: 40 }} c={colors['blue-button']} fw={800} lh={1.2}>
|
||||||
Penanganan Darurat
|
Penanganan Darurat
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="md" c="dimmed" mt={4}>
|
<Text fz="md" mt={4}>
|
||||||
Informasi cepat dan jelas untuk situasi darurat kesehatan
|
Informasi cepat dan jelas untuk situasi darurat kesehatan
|
||||||
</Text>
|
</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
@@ -104,36 +105,28 @@ function Page() {
|
|||||||
onMouseLeave={(e) => (e.currentTarget.style.transform = 'translateY(0)')}
|
onMouseLeave={(e) => (e.currentTarget.style.transform = 'translateY(0)')}
|
||||||
>
|
>
|
||||||
<Stack align="center" gap="md">
|
<Stack align="center" gap="md">
|
||||||
<Center>
|
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: 180, // 🔥 tinggi fix biar semua seragam
|
aspectRatio: '16/9',
|
||||||
borderRadius: 12,
|
borderRadius: '12px',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
backgroundColor: '#f0f2f5', // fallback kalau gambar loading
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={v.image?.link || '/img/default.png'}
|
src={v.image.link}
|
||||||
alt={v.name}
|
alt={v.name}
|
||||||
fit="cover"
|
fit="cover"
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
style={{
|
style={{
|
||||||
objectFit: 'cover',
|
width: '100%',
|
||||||
objectPosition: 'center',
|
height: '100%',
|
||||||
transition: 'transform 0.4s ease',
|
transition: 'transform 0.4s ease',
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => (e.currentTarget.style.transform = 'scale(1.05)')}
|
|
||||||
onMouseLeave={(e) => (e.currentTarget.style.transform = 'scale(1)')}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
</Center>
|
|
||||||
<Stack gap={4} w="100%">
|
<Stack gap={4} w="100%">
|
||||||
<Text
|
<Text
|
||||||
fz="lg"
|
fz="lg"
|
||||||
@@ -146,13 +139,22 @@ function Page() {
|
|||||||
</Text>
|
</Text>
|
||||||
<Box>
|
<Box>
|
||||||
<Text
|
<Text
|
||||||
fz="sm"
|
fz="md"
|
||||||
c="dimmed"
|
lineClamp={3}
|
||||||
lineClamp={4}
|
|
||||||
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Button
|
||||||
|
radius="xl"
|
||||||
|
size="md"
|
||||||
|
component="a"
|
||||||
|
href={`/darmasaba/kesehatan/penanganan-darurat/${v.id}`}
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
c="white"
|
||||||
|
>
|
||||||
|
Lihat Detail
|
||||||
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export default function DetailPosyanduUser() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
{/* Konten utama */}
|
||||||
<Paper
|
<Paper
|
||||||
withBorder
|
withBorder
|
||||||
p="xl"
|
p="xl"
|
||||||
@@ -85,15 +86,15 @@ export default function DetailPosyanduUser() {
|
|||||||
|
|
||||||
{/* Info utama */}
|
{/* Info utama */}
|
||||||
<Stack gap="sm" mt="md">
|
<Stack gap="sm" mt="md">
|
||||||
<Flex align="flex-start" gap="xs">
|
<Flex align="center" gap="xs">
|
||||||
<IconPhone size={18} stroke={1.5} style={{ marginTop: 3 }} />
|
<IconPhone size={18} stroke={1.5} />
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
{data.nomor || 'Nomor tidak tersedia'}
|
{data.nomor || 'Nomor tidak tersedia'}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex align="flex-start" gap="xs">
|
<Flex align="center" gap="xs">
|
||||||
<IconCalendar size={18} stroke={1.5} style={{ marginTop: 3 }} />
|
<IconCalendar size={18} stroke={1.5} />
|
||||||
<Text
|
<Text
|
||||||
fz="sm"
|
fz="sm"
|
||||||
c="dimmed"
|
c="dimmed"
|
||||||
@@ -102,8 +103,8 @@ export default function DetailPosyanduUser() {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex align="flex-start" gap="xs">
|
<Flex align="center" gap="xs">
|
||||||
<IconInfoCircle size={18} stroke={1.5} style={{ marginTop: 3 }} />
|
<IconInfoCircle size={18} stroke={1.5} />
|
||||||
<Text
|
<Text
|
||||||
fz="sm"
|
fz="sm"
|
||||||
c="dimmed"
|
c="dimmed"
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pos="relative" bg={colors.Bg} py="xl" gap="xl">
|
<Stack bg={colors.Bg} py="xl" gap="xl">
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Paper
|
<Paper
|
||||||
px={{ base: 'md', md: 100 }}
|
px={{ base: 'md', md: 100 }}
|
||||||
py="xl"
|
py="xl"
|
||||||
@@ -70,7 +71,7 @@ function Page() {
|
|||||||
<Group gap="xl">
|
<Group gap="xl">
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<Tooltip label="Tanggal dibuat" withArrow>
|
<Tooltip label="Tanggal dibuat" withArrow>
|
||||||
<IconCalendar size={20} stroke={1.5} />
|
<IconCalendar color='gray' size={20} stroke={1.5} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{state.findUnique.data.createdAt
|
{state.findUnique.data.createdAt
|
||||||
@@ -84,13 +85,14 @@ function Page() {
|
|||||||
</Group>
|
</Group>
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<Tooltip label="Dibuat oleh" withArrow>
|
<Tooltip label="Dibuat oleh" withArrow>
|
||||||
<IconUser size={20} stroke={1.5} />
|
<IconUser color='gray' size={20} stroke={1.5} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Text size="sm" c="dimmed">Admin Desa</Text>
|
<Text size="sm" c="dimmed">Admin Desa</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import programKesehatan from "@/app/admin/(dashboard)/_state/kesehatan/program-kesehatan/programKesehatan";
|
||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -15,9 +16,9 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Tooltip,
|
Transition
|
||||||
Transition,
|
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { useDebouncedValue, useShallowEffect } from "@mantine/hooks";
|
||||||
import {
|
import {
|
||||||
IconBarbell,
|
IconBarbell,
|
||||||
IconCalendar,
|
IconCalendar,
|
||||||
@@ -26,12 +27,10 @@ import {
|
|||||||
IconUser,
|
IconUser,
|
||||||
IconUsersGroup,
|
IconUsersGroup,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import BackButton from "../../desa/layanan/_com/BackButto";
|
|
||||||
import { useProxy } from "valtio/utils";
|
|
||||||
import programKesehatan from "@/app/admin/(dashboard)/_state/kesehatan/program-kesehatan/programKesehatan";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { useDebouncedValue, useShallowEffect } from "@mantine/hooks";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useProxy } from "valtio/utils";
|
||||||
|
import BackButton from "../../desa/layanan/_com/BackButto";
|
||||||
|
|
||||||
const manfaatProgram = [
|
const manfaatProgram = [
|
||||||
{
|
{
|
||||||
@@ -88,7 +87,7 @@ export default function Page() {
|
|||||||
>
|
>
|
||||||
Program Kesehatan Desa
|
Program Kesehatan Desa
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="lg" c="dimmed" mt="xs">
|
<Text fz="lg" mt="xs">
|
||||||
Temukan berbagai program kesehatan untuk mendukung kualitas hidup
|
Temukan berbagai program kesehatan untuk mendukung kualitas hidup
|
||||||
masyarakat Darmasaba.
|
masyarakat Darmasaba.
|
||||||
</Text>
|
</Text>
|
||||||
@@ -126,18 +125,37 @@ export default function Page() {
|
|||||||
className="hover-scale"
|
className="hover-scale"
|
||||||
>
|
>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Box h={180} w="100%">
|
<Center>
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: 180, // 🔥 tinggi fix biar semua seragam
|
||||||
|
borderRadius: 12,
|
||||||
|
overflow: 'hidden',
|
||||||
|
position: 'relative',
|
||||||
|
backgroundColor: '#f0f2f5', // fallback kalau gambar loading
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
src={v.image?.link}
|
src={v.image?.link || '/img/default.png'}
|
||||||
alt={v.name}
|
alt={v.name}
|
||||||
radius="xl"
|
|
||||||
w="100%"
|
|
||||||
h="100%"
|
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
|
style={{
|
||||||
|
objectFit: 'cover',
|
||||||
|
objectPosition: 'center',
|
||||||
|
transition: 'transform 0.4s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => (e.currentTarget.style.transform = 'scale(1.05)')}
|
||||||
|
onMouseLeave={(e) => (e.currentTarget.style.transform = 'scale(1)')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
|
</Center>
|
||||||
|
|
||||||
<Box px="lg" pb="lg">
|
<Box px="lg" pb="lg">
|
||||||
<Text
|
<Text
|
||||||
fw="bold"
|
fw="bold"
|
||||||
@@ -149,7 +167,7 @@ export default function Page() {
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
fz="sm"
|
fz="sm"
|
||||||
c="dimmed"
|
ta={"justify"}
|
||||||
lineClamp={3}
|
lineClamp={3}
|
||||||
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
dangerouslySetInnerHTML={{ __html: v.deskripsi }}
|
||||||
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
style={{ wordBreak: "break-word", whiteSpace: "normal" }}
|
||||||
@@ -175,7 +193,6 @@ export default function Page() {
|
|||||||
<Text size="sm">Admin Desa</Text>
|
<Text size="sm">Admin Desa</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<Tooltip label="Lihat detail program" withArrow>
|
|
||||||
<Button
|
<Button
|
||||||
mt="lg"
|
mt="lg"
|
||||||
fullWidth
|
fullWidth
|
||||||
@@ -192,7 +209,6 @@ export default function Page() {
|
|||||||
>
|
>
|
||||||
Lihat Detail
|
Lihat Detail
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
@@ -230,7 +246,7 @@ export default function Page() {
|
|||||||
>
|
>
|
||||||
Manfaat Program Kesehatan
|
Manfaat Program Kesehatan
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="lg" c="dimmed" maw={700}>
|
<Text fz="lg" maw={700}>
|
||||||
Program kesehatan Desa Darmasaba berperan penting dalam meningkatkan
|
Program kesehatan Desa Darmasaba berperan penting dalam meningkatkan
|
||||||
kesejahteraan dan kualitas hidup warganya.
|
kesejahteraan dan kualitas hidup warganya.
|
||||||
</Text>
|
</Text>
|
||||||
@@ -260,7 +276,7 @@ export default function Page() {
|
|||||||
<Text ta="center" fw="bold" fz="xl" c={colors["blue-button"]}>
|
<Text ta="center" fw="bold" fz="xl" c={colors["blue-button"]}>
|
||||||
{v.title}
|
{v.title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text ta="center" fz="sm" c="dimmed">
|
<Text ta="center" fz="sm">
|
||||||
{v.desc}
|
{v.desc}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ function Page() {
|
|||||||
<Text fz={{ base: "2rem", md: "2.5rem" }} c={colors["blue-button"]} fw="bold">
|
<Text fz={{ base: "2rem", md: "2.5rem" }} c={colors["blue-button"]} fw="bold">
|
||||||
Daftar Puskesmas
|
Daftar Puskesmas
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="md">
|
||||||
Temukan informasi lengkap mengenai layanan, kontak, dan lokasi Puskesmas Darmasaba
|
Temukan informasi lengkap mengenai layanan, kontak, dan lokasi Puskesmas Darmasaba
|
||||||
</Text>
|
</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
@@ -93,20 +93,23 @@ function Page() {
|
|||||||
<Text fw={600} fz="lg" lineClamp={1}>{v.name}</Text>
|
<Text fw={600} fz="lg" lineClamp={1}>{v.name}</Text>
|
||||||
<Badge color="blue" variant="light" radius="sm" fz="xs">Aktif</Badge>
|
<Badge color="blue" variant="light" radius="sm" fz="xs">Aktif</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
<Stack gap={4}>
|
<Stack gap={6}>
|
||||||
<Group gap="xs">
|
<Group gap="xs" align="flex-start" wrap="nowrap">
|
||||||
<IconMapPin size={16} />
|
<Box pt={2}><IconMapPin size={16} /></Box>
|
||||||
<Text fz="sm" c="dimmed" lineClamp={2}>{v.alamat}</Text>
|
<Text fz="sm" c="dimmed">{v.alamat}</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Group gap="xs">
|
|
||||||
<IconPhone size={16} />
|
<Group gap="xs" align="flex-start" wrap="nowrap">
|
||||||
|
<Box pt={2}><IconPhone size={16} /></Box>
|
||||||
<Text fz="sm" c="dimmed">{v.kontak.kontakPuskesmas}</Text>
|
<Text fz="sm" c="dimmed">{v.kontak.kontakPuskesmas}</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Group gap="xs">
|
|
||||||
<IconMail size={16} />
|
<Group gap="xs" align="flex-start" wrap="nowrap">
|
||||||
|
<Box pt={2}><IconMail size={16} /></Box>
|
||||||
<Text fz="sm" c="dimmed">{v.kontak.email}</Text>
|
<Text fz="sm" c="dimmed">{v.kontak.email}</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Anchor
|
<Anchor
|
||||||
href={`/darmasaba/kesehatan/puskesmas/${v.id}`}
|
href={`/darmasaba/kesehatan/puskesmas/${v.id}`}
|
||||||
fz="sm"
|
fz="sm"
|
||||||
|
|||||||
@@ -71,8 +71,11 @@ function Page() {
|
|||||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
<Text fz="lg" c={'black'}>
|
<Text fz="md" >
|
||||||
Desa Darmasaba menjaga dan mengembangkan lingkungan demi kesejahteraan warganya. Fokus utama meliputi penghijauan, pengelolaan sampah, dan perlindungan kawasan hijau.
|
Desa Darmasaba menjaga dan mengembangkan lingkungan demi kesejahteraan warganya.
|
||||||
|
</Text>
|
||||||
|
<Text fz="md">
|
||||||
|
Fokus utama meliputi penghijauan, pengelolaan sampah, dan perlindungan kawasan hijau.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// Create a new component: components/EdukasiCard.tsx
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Box, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
interface EdukasiCardProps {
|
||||||
|
icon: ReactNode;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EdukasiCard({ icon, title, description, color = '#1e88e5' }: EdukasiCardProps) {
|
||||||
|
return (
|
||||||
|
<Paper
|
||||||
|
p={{ base: 'md', md: 'lg' }}
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
withBorder
|
||||||
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
transition: 'transform 0.2s, box-shadow 0.2s',
|
||||||
|
'&:hover': {
|
||||||
|
transform: 'translateY(-4px)',
|
||||||
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack h="100%" justify="space-between" gap="md">
|
||||||
|
<Box>
|
||||||
|
<Stack align="center" gap="xs" mb="md">
|
||||||
|
<Box style={{ color }}>{icon}</Box>
|
||||||
|
<Text
|
||||||
|
fz={{ base: 'h5', md: 'h4' }}
|
||||||
|
fw={700}
|
||||||
|
c={color}
|
||||||
|
ta="center"
|
||||||
|
lineClamp={2}
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
minHeight: '3.5rem',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{ __html: title }}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Text
|
||||||
|
size="sm"
|
||||||
|
style={{
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
lineHeight: 1.6,
|
||||||
|
color: 'var(--mantine-color-gray-7)'
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{ __html: description }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,126 +1,103 @@
|
|||||||
'use client'
|
'use client';
|
||||||
import stateEdukasiLingkungan from '@/app/admin/(dashboard)/_state/lingkungan/edukasi-lingkungan';
|
|
||||||
import colors from '@/con/colors';
|
import { Box, Container, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
import { Box, Paper, SimpleGrid, Skeleton, Stack, Text, Tooltip } from '@mantine/core';
|
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconLeaf, IconPlant2, IconRecycle } from '@tabler/icons-react';
|
import { IconLeaf, IconPlant2, IconRecycle } from '@tabler/icons-react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
|
|
||||||
function Page() {
|
import stateEdukasiLingkungan from '@/app/admin/(dashboard)/_state/lingkungan/edukasi-lingkungan';
|
||||||
const tujuan = useProxy(stateEdukasiLingkungan.stateTujuanEdukasi.findById)
|
import colors from '@/con/colors';
|
||||||
const materi = useProxy(stateEdukasiLingkungan.stateMateriEdukasiLingkungan.findById)
|
import { EdukasiCard } from './component/edukasiCard';
|
||||||
const contoh = useProxy(stateEdukasiLingkungan.stateContohEdukasiLingkungan.findById)
|
|
||||||
|
function LoadingSkeleton() {
|
||||||
|
return (
|
||||||
|
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg">
|
||||||
|
{[1, 2, 3].map((item) => (
|
||||||
|
<Skeleton key={item} height={300} radius="md" />
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EdukasiLingkunganPage() {
|
||||||
|
const tujuan = useProxy(stateEdukasiLingkungan.stateTujuanEdukasi.findById);
|
||||||
|
const materi = useProxy(stateEdukasiLingkungan.stateMateriEdukasiLingkungan.findById);
|
||||||
|
const contoh = useProxy(stateEdukasiLingkungan.stateContohEdukasiLingkungan.findById);
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
tujuan.load('edit')
|
tujuan.load('edit');
|
||||||
materi.load('edit')
|
materi.load('edit');
|
||||||
contoh.load('edit')
|
contoh.load('edit');
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
if (tujuan.loading || !tujuan.data || materi.loading || !materi.data || contoh.loading || !contoh.data) {
|
const isLoading = tujuan.loading || !tujuan.data ||
|
||||||
|
materi.loading || !materi.data ||
|
||||||
|
contoh.loading || !contoh.data;
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<Stack py={20}>
|
<Stack py="xl" px={{ base: 'md', md: 'xl' }}>
|
||||||
<Skeleton radius="md" height={600} />
|
<BackButton />
|
||||||
|
<LoadingSkeleton />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
|
<Stack bg={colors.Bg} py="xl" gap="xl" px={{ base: 'md', md: 'xl' }}>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box px={{ base: 'md', md: 100 }} pb={20}>
|
<Container size="lg" ta="center">
|
||||||
<Text ta={'center'} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw="bold">
|
<Text
|
||||||
|
component="h1"
|
||||||
|
fz={{ base: 'h2', md: '2.5rem' }}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
fw={700}
|
||||||
|
mb="md"
|
||||||
|
>
|
||||||
Edukasi Lingkungan
|
Edukasi Lingkungan
|
||||||
</Text>
|
</Text>
|
||||||
<Text ta={'center'} fz="h4" c="black">
|
<Text
|
||||||
|
fz={{ base: 'md', md: 'lg' }}
|
||||||
|
maw={800}
|
||||||
|
mx="auto"
|
||||||
|
>
|
||||||
Program edukasi ini membimbing masyarakat untuk peduli dan bertanggung jawab terhadap alam,
|
Program edukasi ini membimbing masyarakat untuk peduli dan bertanggung jawab terhadap alam,
|
||||||
meningkatkan kesehatan, kenyamanan, dan keberlanjutan hidup bersama.
|
meningkatkan kesehatan, kenyamanan, dan keberlanjutan hidup bersama.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Container>
|
||||||
|
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Container size="xl">
|
||||||
<SimpleGrid cols={{ base: 1, md: 3 }} spacing="lg" style={{ alignItems: 'stretch' }}>
|
<SimpleGrid
|
||||||
{/* Tujuan Edukasi Lingkungan */}
|
cols={{ base: 1, sm: 2, lg: 3 }}
|
||||||
<Box style={{ display: 'flex', height: '100%' }}>
|
spacing="xl"
|
||||||
<Paper p={20} bg={colors['white-trans-1']} shadow="md" radius="md" style={{ width: '100%', display: 'flex', flexDirection: 'column' }}>
|
verticalSpacing={{ base: 'md', md: 'xl' }}
|
||||||
<Stack gap="md">
|
>
|
||||||
<Box>
|
<EdukasiCard
|
||||||
<Tooltip label={<Text fz={"sm"} c={"white"} dangerouslySetInnerHTML={{ __html: tujuan.data?.judul || '' }} /> } position="top" withArrow>
|
icon={<IconLeaf size={45} />}
|
||||||
<Stack gap={4} align="center">
|
title={tujuan.data?.judul || ''}
|
||||||
<IconLeaf size={28} color={colors['blue-button']} />
|
description={tujuan.data?.deskripsi || ''}
|
||||||
<Text dangerouslySetInnerHTML={{ __html: tujuan.data?.judul || '' }} fz="h3" fw="bold" c={colors['blue-button']} ta="center" />
|
color={colors['blue-button']}
|
||||||
</Stack>
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
wordBreak: "break-word",
|
|
||||||
whiteSpace: "normal",
|
|
||||||
flexGrow: 1
|
|
||||||
}}
|
|
||||||
dangerouslySetInnerHTML={{ __html: tujuan.data?.deskripsi || '' }}
|
|
||||||
/>
|
/>
|
||||||
<Box style={{ flexGrow: 1 }} />
|
|
||||||
</Stack>
|
<EdukasiCard
|
||||||
</Paper>
|
icon={<IconRecycle size={45} />}
|
||||||
</Box>
|
title={materi.data?.judul || ''}
|
||||||
{/* Materi Edukasi Lingkungan */}
|
description={materi.data?.deskripsi || ''}
|
||||||
<Box style={{ display: 'flex', height: '100%' }}>
|
color={colors['blue-button']}
|
||||||
<Paper p={20} bg={colors['white-trans-1']} shadow="md" radius="md">
|
|
||||||
<Stack gap="md">
|
|
||||||
<Box>
|
|
||||||
<Tooltip label={materi.data?.judul} position="top" withArrow>
|
|
||||||
<Stack gap={4} align="center">
|
|
||||||
<IconRecycle size={28} color={colors['blue-button']} />
|
|
||||||
<Text fz="h3" fw="bold" c={colors['blue-button']} ta="center" dangerouslySetInnerHTML={{ __html: materi.data?.judul || '' }} />
|
|
||||||
</Stack>
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
wordBreak: "break-word",
|
|
||||||
whiteSpace: "normal",
|
|
||||||
flexGrow: 1
|
|
||||||
}}
|
|
||||||
dangerouslySetInnerHTML={{ __html: materi.data?.deskripsi || '' }}
|
|
||||||
/>
|
/>
|
||||||
<Box style={{ flexGrow: 1 }} />
|
|
||||||
</Stack>
|
<EdukasiCard
|
||||||
</Paper>
|
icon={<IconPlant2 size={45} />}
|
||||||
</Box>
|
title={contoh.data?.judul || ''}
|
||||||
{/* Contoh Edukasi Lingkungan */}
|
description={contoh.data?.deskripsi || ''}
|
||||||
<Box style={{ display: 'flex', height: '100%' }}>
|
color={colors['blue-button']}
|
||||||
<Paper p={20} bg={colors['white-trans-1']} shadow="md" radius="md">
|
|
||||||
<Stack gap="md">
|
|
||||||
<Box>
|
|
||||||
<Tooltip label={contoh.data?.judul} position="top" withArrow>
|
|
||||||
<Stack gap={4} align="center">
|
|
||||||
<IconPlant2 size={28} color={colors['blue-button']} />
|
|
||||||
<Text fz="h3" fw="bold" c={colors['blue-button']} ta="center">
|
|
||||||
{contoh.data?.judul}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
wordBreak: "break-word",
|
|
||||||
whiteSpace: "normal",
|
|
||||||
flexGrow: 1
|
|
||||||
}}
|
|
||||||
dangerouslySetInnerHTML={{ __html: contoh.data?.deskripsi || '' }}
|
|
||||||
/>
|
/>
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Box>
|
</Container>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
|
||||||
|
|||||||
@@ -1,323 +1,5 @@
|
|||||||
// 'use client'
|
|
||||||
// import colors from '@/con/colors';
|
|
||||||
// import { Box, Container, Grid, GridCol, Stack, Tabs, TabsList, TabsTab, Text, TextInput } from '@mantine/core';
|
|
||||||
// import { IconSearch } from '@tabler/icons-react';
|
|
||||||
// import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
||||||
// import React, { useEffect, useState } from 'react';
|
|
||||||
// import BackButton from '../../../desa/layanan/_com/BackButto';
|
|
||||||
|
|
||||||
|
|
||||||
// type HeaderSearchProps = {
|
|
||||||
// placeholder?: string;
|
|
||||||
// searchIcon?: React.ReactNode;
|
|
||||||
// value?: string;
|
|
||||||
// onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
// children?: React.ReactNode;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// function LayoutTabsGotongRoyong({
|
|
||||||
// children,
|
|
||||||
// placeholder = "pencarian",
|
|
||||||
// searchIcon = <IconSearch size={20} />
|
|
||||||
// }: HeaderSearchProps) {
|
|
||||||
// const router = useRouter();
|
|
||||||
// const pathname = usePathname();
|
|
||||||
// const searchParams = useSearchParams();
|
|
||||||
|
|
||||||
// // Get active tab from URL path
|
|
||||||
// const activeTab = pathname.split('/').pop() || 'semua';
|
|
||||||
|
|
||||||
// // Get initial search value from URL
|
|
||||||
// const initialSearch = searchParams.get('search') || '';
|
|
||||||
// const [searchValue, setSearchValue] = useState(initialSearch);
|
|
||||||
// const [searchTimeout, setSearchTimeout] = useState<number | null>(null);
|
|
||||||
|
|
||||||
// // Update active tab state when pathname changes
|
|
||||||
// const [activeTabState, setActiveTabState] = useState(activeTab);
|
|
||||||
// useEffect(() => {
|
|
||||||
// setActiveTabState(activeTab);
|
|
||||||
// }, [activeTab]);
|
|
||||||
|
|
||||||
// // Clean up timeouts on unmount
|
|
||||||
// useEffect(() => {
|
|
||||||
// return () => {
|
|
||||||
// if (searchTimeout !== null) {
|
|
||||||
// clearTimeout(searchTimeout);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }, [searchTimeout]);
|
|
||||||
|
|
||||||
// // Handle search input change with debounce
|
|
||||||
// const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
// const value = event.target.value;
|
|
||||||
// setSearchValue(value);
|
|
||||||
|
|
||||||
// // Clear previous timeout
|
|
||||||
// if (searchTimeout !== null) {
|
|
||||||
// clearTimeout(searchTimeout);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Set new timeout
|
|
||||||
// const newTimeout = window.setTimeout(() => {
|
|
||||||
// const params = new URLSearchParams(searchParams.toString());
|
|
||||||
|
|
||||||
// if (value) {
|
|
||||||
// params.set('search', value);
|
|
||||||
// } else {
|
|
||||||
// params.delete('search');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Only update URL if the search value has actually changed
|
|
||||||
// if (params.toString() !== searchParams.toString()) {
|
|
||||||
// router.push(`/darmasaba/lingkungan/gotong-royong/${activeTab}?${params.toString()}`);
|
|
||||||
// }
|
|
||||||
// }, 500); // 500ms debounce delay
|
|
||||||
|
|
||||||
// setSearchTimeout(newTimeout);
|
|
||||||
// };
|
|
||||||
// const tabs = [
|
|
||||||
// {
|
|
||||||
// label: "Semua",
|
|
||||||
// value: "semua",
|
|
||||||
// href: "/darmasaba/lingkungan/gotong-royong/semua"
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// label: "Kebersihan",
|
|
||||||
// value: "kebersihan",
|
|
||||||
// href: "/darmasaba/lingkungan/gotong-royong/kebersihan"
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// label: "Infrastruktur",
|
|
||||||
// value: "infrastruktur",
|
|
||||||
// href: "/darmasaba/lingkungan/gotong-royong/infrastruktur"
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// label: "Sosial",
|
|
||||||
// value: "sosial",
|
|
||||||
// href: "/darmasaba/lingkungan/gotong-royong/sosial"
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// label: "Lingkungan",
|
|
||||||
// value: "lingkungan",
|
|
||||||
// href: "/darmasaba/lingkungan/gotong-royong/lingkungan"
|
|
||||||
// }
|
|
||||||
// ];
|
|
||||||
// const handleTabChange = (value: string | null) => {
|
|
||||||
// if (!value) return;
|
|
||||||
// const tab = tabs.find(t => t.value === value);
|
|
||||||
// if (tab) {
|
|
||||||
// const params = new URLSearchParams(searchParams.toString());
|
|
||||||
// router.push(`/darmasaba/lingkungan/gotong-royong/${value}${params.toString() ? `?${params.toString()}` : ''}`);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
|
|
||||||
// {/* Header */}
|
|
||||||
// <Box px={{ base: "md", md: 100 }}>
|
|
||||||
// <BackButton />
|
|
||||||
// </Box>
|
|
||||||
// <Container size="lg" px="md">
|
|
||||||
// <Stack align="center" gap="0" >
|
|
||||||
// <Text fz={{ base: "2rem", md: "3.4rem" }} c={colors["blue-button"]} fw="bold" ta="center">
|
|
||||||
// Gotong Royong Desa Darmasaba
|
|
||||||
// </Text>
|
|
||||||
// <Text ta="center" px="md">
|
|
||||||
// Gotong royong rutin dilakukan oleh warga desa untuk meningkatkan kualitas hidup dan kesejahteraan masyarakat Desa Darmasaba
|
|
||||||
// </Text>
|
|
||||||
// </Stack>
|
|
||||||
// </Container>
|
|
||||||
|
|
||||||
// <Tabs
|
|
||||||
// color={colors['blue-button']}
|
|
||||||
// variant="pills"
|
|
||||||
// value={activeTabState}
|
|
||||||
// onChange={handleTabChange}
|
|
||||||
// >
|
|
||||||
// <Box px={{ base: "md", md: 100 }} py="md" bg={colors['BG-trans']}>
|
|
||||||
// <Grid>
|
|
||||||
// <GridCol span={{ base: 12, md: 9, lg: 8, xl: 9 }}>
|
|
||||||
// <TabsList>
|
|
||||||
// {tabs.map((tab, index) => (
|
|
||||||
// <TabsTab
|
|
||||||
// key={index}
|
|
||||||
// value={tab.value}
|
|
||||||
// onClick={() => router.push(tab.href)}
|
|
||||||
// >
|
|
||||||
// {tab.label}
|
|
||||||
// </TabsTab>
|
|
||||||
// ))}
|
|
||||||
// </TabsList>
|
|
||||||
// </GridCol>
|
|
||||||
// <GridCol span={{ base: 12, md: 3, lg: 4, xl: 3 }}>
|
|
||||||
// <TextInput
|
|
||||||
// radius="lg"
|
|
||||||
// placeholder={placeholder}
|
|
||||||
// leftSection={searchIcon}
|
|
||||||
// w="100%"
|
|
||||||
// value={searchValue}
|
|
||||||
// onChange={handleSearchChange}
|
|
||||||
// />
|
|
||||||
// </GridCol>
|
|
||||||
// </Grid>
|
|
||||||
// </Box>
|
|
||||||
|
|
||||||
// {children}
|
|
||||||
// </Tabs>
|
|
||||||
// </Stack>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export default LayoutTabsGotongRoyong;
|
|
||||||
|
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
// 'use client'
|
|
||||||
// import colors from '@/con/colors';
|
|
||||||
// import { Box, Group, Stack, Tabs, TabsList, TabsTab, Text, TextInput } from '@mantine/core';
|
|
||||||
// import { IconSearch } from '@tabler/icons-react';
|
|
||||||
// import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
||||||
// import React, { useEffect, useState } from 'react';
|
|
||||||
// import BackButton from '../../layanan/_com/BackButto';
|
|
||||||
|
|
||||||
// type HeaderSearchProps = {
|
|
||||||
// placeholder?: string;
|
|
||||||
// searchIcon?: React.ReactNode;
|
|
||||||
// value?: string;
|
|
||||||
// onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
// children?: React.ReactNode;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// function LayoutTabsBerita({
|
|
||||||
// children,
|
|
||||||
// placeholder = "pencarian",
|
|
||||||
// searchIcon = <IconSearch size={20} />
|
|
||||||
// }: HeaderSearchProps) {
|
|
||||||
// const router = useRouter();
|
|
||||||
// const pathname = usePathname();
|
|
||||||
// const searchParams = useSearchParams();
|
|
||||||
|
|
||||||
// const activeTab = pathname.split('/').pop() || 'semua';
|
|
||||||
// const initialSearch = searchParams.get('search') || '';
|
|
||||||
// const [searchValue, setSearchValue] = useState(initialSearch);
|
|
||||||
// const [searchTimeout, setSearchTimeout] = useState<number | null>(null);
|
|
||||||
|
|
||||||
// const [activeTabState, setActiveTabState] = useState(activeTab);
|
|
||||||
// useEffect(() => {
|
|
||||||
// setActiveTabState(activeTab);
|
|
||||||
// }, [activeTab]);
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// return () => {
|
|
||||||
// if (searchTimeout !== null) clearTimeout(searchTimeout);
|
|
||||||
// };
|
|
||||||
// }, [searchTimeout]);
|
|
||||||
|
|
||||||
// const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
// const value = event.target.value;
|
|
||||||
// setSearchValue(value);
|
|
||||||
|
|
||||||
// if (searchTimeout !== null) clearTimeout(searchTimeout);
|
|
||||||
|
|
||||||
// const newTimeout = window.setTimeout(() => {
|
|
||||||
// const params = new URLSearchParams(searchParams.toString());
|
|
||||||
|
|
||||||
// if (value) params.set('search', value);
|
|
||||||
// else params.delete('search');
|
|
||||||
|
|
||||||
// if (params.toString() !== searchParams.toString()) {
|
|
||||||
// router.push(`/darmasaba/desa/berita/${activeTab}?${params.toString()}`);
|
|
||||||
// }
|
|
||||||
// }, 500);
|
|
||||||
|
|
||||||
// setSearchTimeout(newTimeout);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const tabs = [
|
|
||||||
// { label: "Semua", value: "semua", href: "/darmasaba/desa/berita/semua" },
|
|
||||||
// { label: "Budaya", value: "budaya", href: "/darmasaba/desa/berita/budaya" },
|
|
||||||
// { label: "Pemerintahan", value: "pemerintahan", href: "/darmasaba/desa/berita/pemerintahan" },
|
|
||||||
// { label: "Ekonomi", value: "ekonomi", href: "/darmasaba/desa/berita/ekonomi" },
|
|
||||||
// { label: "Pembangunan", value: "pembangunan", href: "/darmasaba/desa/berita/pembangunan" },
|
|
||||||
// { label: "Sosial", value: "sosial", href: "/darmasaba/desa/berita/sosial" },
|
|
||||||
// { label: "Teknologi", value: "teknologi", href: "/darmasaba/desa/berita/teknologi" },
|
|
||||||
// ];
|
|
||||||
|
|
||||||
// const handleTabChange = (value: string | null) => {
|
|
||||||
// if (!value) return;
|
|
||||||
// const tab = tabs.find(t => t.value === value);
|
|
||||||
// if (tab) {
|
|
||||||
// const params = new URLSearchParams(searchParams.toString());
|
|
||||||
// router.push(`/darmasaba/desa/berita/${value}${params.toString() ? `?${params.toString()}` : ''}`);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <Stack pos="relative" bg={colors.Bg} py="xl" gap="22">
|
|
||||||
// {/* Header */}
|
|
||||||
// <Box px={{ base: "md", md: 100 }}>
|
|
||||||
// <BackButton />
|
|
||||||
// </Box>
|
|
||||||
|
|
||||||
// <Box px={{ base: 'md', md: 100 }}>
|
|
||||||
// <Group justify='space-between' align="center">
|
|
||||||
// <Stack gap="0">
|
|
||||||
// <Text fz={{ base: "2rem", md: "3.4rem" }} c={colors["blue-button"]} fw="bold" >
|
|
||||||
// Portal Berita Darmasaba
|
|
||||||
// </Text>
|
|
||||||
// <Text>
|
|
||||||
// Temukan berbagai potensi dan keunggulan yang dimiliki Desa Darmasaba
|
|
||||||
// </Text>
|
|
||||||
// </Stack>
|
|
||||||
// <Box>
|
|
||||||
// <TextInput
|
|
||||||
// radius="lg"
|
|
||||||
// placeholder={placeholder}
|
|
||||||
// leftSection={searchIcon}
|
|
||||||
// w="100%"
|
|
||||||
// value={searchValue}
|
|
||||||
// onChange={handleSearchChange}
|
|
||||||
// />
|
|
||||||
// </Box>
|
|
||||||
// </Group>
|
|
||||||
// </Box>
|
|
||||||
|
|
||||||
// <Tabs
|
|
||||||
// color={colors['blue-button']}
|
|
||||||
// variant="pills"
|
|
||||||
// value={activeTabState}
|
|
||||||
// onChange={handleTabChange}
|
|
||||||
// >
|
|
||||||
// <Box px={{ base: "md", md: 100 }} py="md" bg={colors['BG-trans']}>
|
|
||||||
// {/* SCROLLABLE TABS */}
|
|
||||||
// <Box style={{ overflowX: 'auto', whiteSpace: 'nowrap' }}>
|
|
||||||
// <TabsList style={{ display: 'flex', flexWrap: 'nowrap', gap: '0.5rem' }}>
|
|
||||||
// {tabs.map((tab, index) => (
|
|
||||||
// <TabsTab
|
|
||||||
// key={index}
|
|
||||||
// value={tab.value}
|
|
||||||
// onClick={() => router.push(tab.href)}
|
|
||||||
// style={{
|
|
||||||
// flex: '0 0 auto', // Prevent shrinking
|
|
||||||
// minWidth: 100, // optional: makes them touch-friendly
|
|
||||||
// textAlign: 'center'
|
|
||||||
// }}
|
|
||||||
// >
|
|
||||||
// {tab.label}
|
|
||||||
// </TabsTab>
|
|
||||||
// ))}
|
|
||||||
// </TabsList>
|
|
||||||
// </Box>
|
|
||||||
// </Box>
|
|
||||||
|
|
||||||
// {children}
|
|
||||||
// </Tabs>
|
|
||||||
// </Stack>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export default LayoutTabsBerita;
|
|
||||||
|
|
||||||
|
|
||||||
'use client'
|
'use client'
|
||||||
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
|
import gotongRoyongState from '@/app/admin/(dashboard)/_state/lingkungan/gotong-royong';
|
||||||
@@ -402,7 +84,7 @@ function LayoutTabsGotongRoyong({ children }: { children: React.ReactNode }) {
|
|||||||
<Text fz={{ base: "2rem", md: "3.4rem" }} c={colors["blue-button"]} fw="bold">
|
<Text fz={{ base: "2rem", md: "3.4rem" }} c={colors["blue-button"]} fw="bold">
|
||||||
Portal Gotong royong Darmasaba
|
Portal Gotong royong Darmasaba
|
||||||
</Text>
|
</Text>
|
||||||
<Text>Temukan berbagai kegiatan lingkungan yang dimiliki Desa Darmasaba</Text>
|
<Text fz="md">Temukan berbagai kegiatan lingkungan yang dimiliki Desa Darmasaba</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Box>
|
<Box>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|||||||
@@ -44,13 +44,12 @@ function Page() {
|
|||||||
<Box style={{ display: 'flex', height: '100%' }}>
|
<Box style={{ display: 'flex', height: '100%' }}>
|
||||||
<Paper
|
<Paper
|
||||||
p="lg"
|
p="lg"
|
||||||
bg="linear-gradient(145deg, #DFE3E8FF 0%, #EFF1F4FF 100%)"
|
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
boxShadow: '0 0 20px rgba(28, 110, 164, 0.5)',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column'
|
flexDirection: 'column',
|
||||||
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap="md" px={20} style={{ height: '100%' }}>
|
<Stack gap="md" px={20} style={{ height: '100%' }}>
|
||||||
@@ -74,13 +73,12 @@ function Page() {
|
|||||||
<Box style={{ display: 'flex', height: '100%' }}>
|
<Box style={{ display: 'flex', height: '100%' }}>
|
||||||
<Paper
|
<Paper
|
||||||
p="lg"
|
p="lg"
|
||||||
bg="linear-gradient(145deg, #DFE3E8FF 0%, #EFF1F4FF 100%)"
|
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
boxShadow: '0 0 20px rgba(28, 110, 164, 0.5)',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column'
|
flexDirection: 'column',
|
||||||
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap="md" px={20} style={{ height: '100%' }}>
|
<Stack gap="md" px={20} style={{ height: '100%' }}>
|
||||||
@@ -105,13 +103,12 @@ function Page() {
|
|||||||
<Box>
|
<Box>
|
||||||
<Paper
|
<Paper
|
||||||
p="lg"
|
p="lg"
|
||||||
bg="linear-gradient(145deg, #DFE3E8FF 0%, #EFF1F4FF 100%)"
|
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
boxShadow: '0 0 20px rgba(28, 110, 164, 0.5)',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column'
|
flexDirection: 'column',
|
||||||
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap="md" px={20} style={{ height: '100%' }}>
|
<Stack gap="md" px={20} style={{ height: '100%' }}>
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ function Page() {
|
|||||||
<Box style={{ alignContent: 'center', alignItems: 'center' }}>
|
<Box style={{ alignContent: 'center', alignItems: 'center' }}>
|
||||||
{iconMap[v.icon] ? React.createElement(iconMap[v.icon]) : null}
|
{iconMap[v.icon] ? React.createElement(iconMap[v.icon]) : null}
|
||||||
</Box>
|
</Box>
|
||||||
<Text fw={'bold'} fz={{ base: "lg", md: "xl" }} c={'black'}>{v.name}</Text>
|
<Text fz={{ base: "lg", md: "xl" }} c={'black'}>{v.name}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -66,8 +66,11 @@ function Page() {
|
|||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
</Box>
|
</Box>
|
||||||
<Text c="dimmed" fz={{ base: 'sm', md: 'lg' }} mt="sm">
|
<Text fz="md" mt="sm">
|
||||||
Mari berpartisipasi menanam dan merawat pohon untuk menciptakan lingkungan hijau, sehat, dan seimbang bagi seluruh warga desa.
|
Mari berpartisipasi menanam dan merawat pohon untuk menciptakan lingkungan hijau,
|
||||||
|
</Text>
|
||||||
|
<Text fz="md">
|
||||||
|
sehat, dan seimbang bagi seluruh warga desa.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: 'md', md: 100 }} pb={60}>
|
<Box px={{ base: 'md', md: 100 }} pb={60}>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import beasiswaDesaState from '@/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa';
|
import beasiswaDesaState from '@/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Center, Group, Image, Modal, Pagination, Paper, Select, SimpleGrid, Skeleton, Stack, Stepper, StepperStep, Text, TextInput, Title } from '@mantine/core';
|
import { Box, Button, Center, Group, Image, Modal, Pagination, Paper, Select, SimpleGrid, Skeleton, Stack, Stepper, Text, TextInput, Title } from '@mantine/core';
|
||||||
import { useDisclosure, useShallowEffect } from '@mantine/hooks';
|
import { useDisclosure, useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconArrowRight, IconCoin, IconInfoCircle, IconSchool, IconUsers } from '@tabler/icons-react';
|
import { IconArrowRight, IconCoin, IconInfoCircle, IconSchool, IconUsers } from '@tabler/icons-react';
|
||||||
|
import { useTransitionRouter } from 'next-view-transitions';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
import { useTransitionRouter } from 'next-view-transitions';
|
|
||||||
|
|
||||||
const dataBeasiswa = [
|
const dataBeasiswa = [
|
||||||
{ id: 1, nama: 'Penerima Beasiswa', jumlah: '250+', icon: IconUsers },
|
{ id: 1, nama: 'Penerima Beasiswa', jumlah: '250+', icon: IconUsers },
|
||||||
@@ -27,7 +27,7 @@ function Page() {
|
|||||||
tempatLahir: "",
|
tempatLahir: "",
|
||||||
tanggalLahir: "",
|
tanggalLahir: "",
|
||||||
jenisKelamin: "",
|
jenisKelamin: "",
|
||||||
kewarganegaraan: "",
|
kewarganegaraan: "WNI",
|
||||||
agama: "",
|
agama: "",
|
||||||
alamatKTP: "",
|
alamatKTP: "",
|
||||||
alamatDomisili: "",
|
alamatDomisili: "",
|
||||||
@@ -50,9 +50,21 @@ function Page() {
|
|||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const timeline = [
|
||||||
|
{ label: "1 Maret 2025", desc: "Pembukaan Pendaftaran", date: new Date("2025-03-01") },
|
||||||
|
{ label: "15 Maret 2025", desc: "Seleksi Administrasi", date: new Date("2025-03-15") },
|
||||||
|
{ label: "1 April 2025", desc: "Tes Potensi Akademik", date: new Date("2025-04-01") },
|
||||||
|
{ label: "15 April 2025", desc: "Wawancara", date: new Date("2025-04-15") },
|
||||||
|
{ label: "1 Mei 2025", desc: "Pengumuman Hasil", date: new Date("2025-05-01") },
|
||||||
|
];
|
||||||
|
|
||||||
const [active, setActive] = useState(1);
|
const [active, setActive] = useState(1);
|
||||||
const nextStep = () => setActive((current) => (current < 5 ? current + 1 : current));
|
useShallowEffect(() => {
|
||||||
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
|
const today = new Date();
|
||||||
|
// cari berapa banyak tanggal yang sudah lewat
|
||||||
|
const doneSteps = timeline.filter(item => today >= item.date).length;
|
||||||
|
setActive(doneSteps); // active step diset sesuai tanggal
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
@@ -139,19 +151,22 @@ function Page() {
|
|||||||
Timeline Pendaftaran
|
Timeline Pendaftaran
|
||||||
</Title>
|
</Title>
|
||||||
<Center>
|
<Center>
|
||||||
<Stepper mt={20} active={active} onStepClick={setActive} orientation="vertical" allowNextStepsSelect={false}>
|
<Stepper
|
||||||
<StepperStep label="1 Maret 2025" description="Pembukaan Pendaftaran" />
|
mt={20}
|
||||||
<StepperStep label="15 Maret 2025" description="Seleksi Administrasi" />
|
active={active}
|
||||||
<StepperStep label="1 April 2025" description="Tes Potensi Akademik" />
|
onStepClick={setActive}
|
||||||
<StepperStep label="15 April 2025" description="Wawancara" />
|
orientation="vertical"
|
||||||
<StepperStep label="1 Mei 2025" description="Pengumuman Hasil" />
|
allowNextStepsSelect={false}
|
||||||
|
>
|
||||||
|
{timeline.map((item, index) => (
|
||||||
|
<Stepper.Step
|
||||||
|
key={index}
|
||||||
|
label={item.label}
|
||||||
|
description={item.desc}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</Stepper>
|
</Stepper>
|
||||||
</Center>
|
</Center>
|
||||||
|
|
||||||
<Group justify="center" mt="xl">
|
|
||||||
<Button variant="default" radius="xl" onClick={prevStep}>Kembali</Button>
|
|
||||||
<Button radius="xl" bg={colors['blue-button']} onClick={nextStep}>Lanjut</Button>
|
|
||||||
</Group>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -194,7 +209,11 @@ function Page() {
|
|||||||
<TextInput
|
<TextInput
|
||||||
label="Kewarganegaraan"
|
label="Kewarganegaraan"
|
||||||
placeholder="Masukkan kewarganegaraan"
|
placeholder="Masukkan kewarganegaraan"
|
||||||
onChange={(val) => { beasiswaDesa.create.form.kewarganegaraan = val.target.value }} />
|
value={beasiswaDesa.create.form.kewarganegaraan || "WNI"} // tampilkan WNI kalau kosong
|
||||||
|
onChange={(e) => {
|
||||||
|
beasiswaDesa.create.form.kewarganegaraan = e.target.value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="Agama"
|
label="Agama"
|
||||||
placeholder="Pilih agama"
|
placeholder="Pilih agama"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default function BeasiswaPage() {
|
|||||||
tempatLahir: "",
|
tempatLahir: "",
|
||||||
tanggalLahir: "",
|
tanggalLahir: "",
|
||||||
jenisKelamin: "",
|
jenisKelamin: "",
|
||||||
kewarganegaraan: "",
|
kewarganegaraan: "WNI",
|
||||||
agama: "",
|
agama: "",
|
||||||
alamatKTP: "",
|
alamatKTP: "",
|
||||||
alamatDomisili: "",
|
alamatDomisili: "",
|
||||||
@@ -71,7 +71,7 @@ export default function BeasiswaPage() {
|
|||||||
<Stack gap="md" maw={600}>
|
<Stack gap="md" maw={600}>
|
||||||
<Group>
|
<Group>
|
||||||
<IconSchool size={30} color={colors["blue-button"]} />
|
<IconSchool size={30} color={colors["blue-button"]} />
|
||||||
<Title order={2}>Program Beasiswa Pendidikan Desa Darmasaba</Title>
|
<Title order={2} c={colors["blue-button"]}>Program Beasiswa Pendidikan Desa Darmasaba</Title>
|
||||||
</Group>
|
</Group>
|
||||||
<Text>
|
<Text>
|
||||||
Program ini bertujuan untuk mendukung pendidikan generasi muda di Desa Darmasaba
|
Program ini bertujuan untuk mendukung pendidikan generasi muda di Desa Darmasaba
|
||||||
@@ -84,7 +84,7 @@ export default function BeasiswaPage() {
|
|||||||
<Container size="lg" py="xl">
|
<Container size="lg" py="xl">
|
||||||
<Group mb="sm">
|
<Group mb="sm">
|
||||||
<IconInfoCircle size={24} color={colors["blue-button"]} />
|
<IconInfoCircle size={24} color={colors["blue-button"]} />
|
||||||
<Title order={3}>Tentang Program</Title>
|
<Title order={3} c={colors["blue-button"]}>Tentang Program</Title>
|
||||||
</Group>
|
</Group>
|
||||||
<Text>
|
<Text>
|
||||||
Program Beasiswa Desa Darmasaba adalah inisiatif pemerintah desa untuk meningkatkan akses
|
Program Beasiswa Desa Darmasaba adalah inisiatif pemerintah desa untuk meningkatkan akses
|
||||||
@@ -94,12 +94,11 @@ export default function BeasiswaPage() {
|
|||||||
|
|
||||||
{/* Tambahkan info tahun berjalan di sini */}
|
{/* Tambahkan info tahun berjalan di sini */}
|
||||||
<Paper mt="md" p="md" radius="lg" shadow="xs" bg="#f8fbff" withBorder>
|
<Paper mt="md" p="md" radius="lg" shadow="xs" bg="#f8fbff" withBorder>
|
||||||
<Text fw={500} c={colors["blue-button"]}>
|
<Text fw={500}>
|
||||||
📅 Periode Beasiswa Tahun 2025
|
Periode Beasiswa Tahun 2025
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
Pendaftaran beasiswa dibuka mulai <strong>1 Januari 2025</strong> dan ditutup pada
|
Pendaftaran beasiswa dibuka mulai <strong>1 Januari 2025</strong> dan ditutup pada <strong>31 Mei 2025</strong>.
|
||||||
<strong>31 Mei 2025</strong>.
|
|
||||||
Pengumuman hasil seleksi akan diumumkan pada pertengahan Juni 2025 melalui website resmi Desa Darmasaba.
|
Pengumuman hasil seleksi akan diumumkan pada pertengahan Juni 2025 melalui website resmi Desa Darmasaba.
|
||||||
</Text>
|
</Text>
|
||||||
</Paper>
|
</Paper>
|
||||||
@@ -109,7 +108,7 @@ export default function BeasiswaPage() {
|
|||||||
<Container size="lg" py="xl">
|
<Container size="lg" py="xl">
|
||||||
<Group mb="sm">
|
<Group mb="sm">
|
||||||
<IconChecklist size={24} color={colors["blue-button"]} />
|
<IconChecklist size={24} color={colors["blue-button"]} />
|
||||||
<Title order={3}>Syarat Pendaftaran</Title>
|
<Title order={3} c={colors["blue-button"]}>Syarat Pendaftaran</Title>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg">
|
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }} spacing="lg">
|
||||||
@@ -140,7 +139,7 @@ export default function BeasiswaPage() {
|
|||||||
<Container size="lg" py="xl">
|
<Container size="lg" py="xl">
|
||||||
<Group mb="sm">
|
<Group mb="sm">
|
||||||
<IconTimeline size={24} color={colors["blue-button"]} />
|
<IconTimeline size={24} color={colors["blue-button"]} />
|
||||||
<Title order={3}>Proses Seleksi</Title>
|
<Title order={3} c={colors["blue-button"]}>Proses Seleksi</Title>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Timeline active={4} bulletSize={24} lineWidth={2}>
|
<Timeline active={4} bulletSize={24} lineWidth={2}>
|
||||||
@@ -148,8 +147,8 @@ export default function BeasiswaPage() {
|
|||||||
<Text c="dimmed" size="sm">
|
<Text c="dimmed" size="sm">
|
||||||
Calon peserta mengisi formulir pendaftaran dan mengunggah dokumen pendukung.
|
Calon peserta mengisi formulir pendaftaran dan mengunggah dokumen pendukung.
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" fw={500} c={colors["blue-button"]} mt={4}>
|
<Text size="sm" fw={500} mt={4}>
|
||||||
⏰ Estimasi waktu: 1 Februari – 31 Mei 2025
|
Estimasi waktu: 1 Februari – 31 Mei 2025
|
||||||
</Text>
|
</Text>
|
||||||
</Timeline.Item>
|
</Timeline.Item>
|
||||||
|
|
||||||
@@ -157,8 +156,8 @@ export default function BeasiswaPage() {
|
|||||||
<Text c="dimmed" size="sm">
|
<Text c="dimmed" size="sm">
|
||||||
Panitia memverifikasi kelengkapan dan validitas berkas.
|
Panitia memverifikasi kelengkapan dan validitas berkas.
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" fw={500} c={colors["blue-button"]} mt={4}>
|
<Text size="sm" fw={500} mt={4}>
|
||||||
⏰ Estimasi waktu: 5–7 hari kerja setelah penutupan pendaftaran
|
Estimasi waktu: 5–7 hari kerja setelah penutupan pendaftaran
|
||||||
</Text>
|
</Text>
|
||||||
</Timeline.Item>
|
</Timeline.Item>
|
||||||
|
|
||||||
@@ -166,8 +165,8 @@ export default function BeasiswaPage() {
|
|||||||
<Text c="dimmed" size="sm">
|
<Text c="dimmed" size="sm">
|
||||||
Peserta yang lolos administrasi akan diundang untuk wawancara langsung dengan tim seleksi.
|
Peserta yang lolos administrasi akan diundang untuk wawancara langsung dengan tim seleksi.
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" fw={500} c={colors["blue-button"]} mt={4}>
|
<Text size="sm" fw={500} mt={4}>
|
||||||
⏰ Estimasi waktu: 7–10 hari kerja setelah pengumuman seleksi administrasi
|
Estimasi waktu: 7–10 hari kerja setelah pengumuman seleksi administrasi
|
||||||
</Text>
|
</Text>
|
||||||
</Timeline.Item>
|
</Timeline.Item>
|
||||||
|
|
||||||
@@ -175,14 +174,14 @@ export default function BeasiswaPage() {
|
|||||||
<Text c="dimmed" size="sm">
|
<Text c="dimmed" size="sm">
|
||||||
Daftar penerima beasiswa diumumkan melalui website resmi Desa Darmasaba.
|
Daftar penerima beasiswa diumumkan melalui website resmi Desa Darmasaba.
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" fw={500} c={colors["blue-button"]} mt={4}>
|
<Text size="sm" fw={500} mt={4}>
|
||||||
⏰ Estimasi waktu: 5 hari kerja setelah tahap wawancara selesai
|
Estimasi waktu: 5 hari kerja setelah tahap wawancara selesai
|
||||||
</Text>
|
</Text>
|
||||||
</Timeline.Item>
|
</Timeline.Item>
|
||||||
</Timeline>
|
</Timeline>
|
||||||
|
|
||||||
<Text c="dimmed" size="sm" mt="lg" ta="center">
|
<Text c="dimmed" size="sm" mt="lg" ta="center">
|
||||||
🗓️ Total estimasi keseluruhan proses: sekitar 3–4 minggu setelah penutupan pendaftaran
|
Total estimasi keseluruhan proses: sekitar 3–4 minggu setelah penutupan pendaftaran
|
||||||
</Text>
|
</Text>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
@@ -191,7 +190,7 @@ export default function BeasiswaPage() {
|
|||||||
<Container size="lg" py="xl">
|
<Container size="lg" py="xl">
|
||||||
<Group mb="sm">
|
<Group mb="sm">
|
||||||
<IconQuote size={24} color={colors["blue-button"]} />
|
<IconQuote size={24} color={colors["blue-button"]} />
|
||||||
<Title order={3}>Cerita Sukses Penerima Beasiswa</Title>
|
<Title order={3} c={colors["blue-button"]}>Cerita Sukses Penerima Beasiswa</Title>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="lg">
|
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="lg">
|
||||||
@@ -219,12 +218,12 @@ export default function BeasiswaPage() {
|
|||||||
<Container size="lg" py="xl" ta="center">
|
<Container size="lg" py="xl" ta="center">
|
||||||
<Group justify="center" mb="sm">
|
<Group justify="center" mb="sm">
|
||||||
<IconUserPlus size={28} color={colors["blue-button"]} />
|
<IconUserPlus size={28} color={colors["blue-button"]} />
|
||||||
<Title order={3}>Siap Bergabung dengan Program Ini?</Title>
|
<Title order={3} c={colors["blue-button"]}>Siap Bergabung dengan Program Ini?</Title>
|
||||||
</Group>
|
</Group>
|
||||||
<Text c="dimmed" mb="md">
|
<Text c="dimmed" mb="md">
|
||||||
Segera daftar dan wujudkan mimpimu bersama Desa Darmasaba.
|
Segera daftar dan wujudkan mimpimu bersama Desa Darmasaba.
|
||||||
</Text>
|
</Text>
|
||||||
<Button onClick={open} size="lg" radius="xl" color="blue">
|
<Button onClick={open} size="lg" radius="xl" bg={colors["blue-button"]}>
|
||||||
Daftar Sekarang
|
Daftar Sekarang
|
||||||
</Button>
|
</Button>
|
||||||
</Container>
|
</Container>
|
||||||
@@ -269,7 +268,11 @@ export default function BeasiswaPage() {
|
|||||||
<TextInput
|
<TextInput
|
||||||
label="Kewarganegaraan"
|
label="Kewarganegaraan"
|
||||||
placeholder="Masukkan kewarganegaraan"
|
placeholder="Masukkan kewarganegaraan"
|
||||||
onChange={(val) => { beasiswaDesa.create.form.kewarganegaraan = val.target.value }} />
|
value={beasiswaDesa.create.form.kewarganegaraan || "WNI"} // tampilkan WNI kalau kosong
|
||||||
|
onChange={(e) => {
|
||||||
|
beasiswaDesa.create.form.kewarganegaraan = e.target.value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="Agama"
|
label="Agama"
|
||||||
placeholder="Pilih agama"
|
placeholder="Pilih agama"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import stateBimbinganBelajarDesa from '@/app/admin/(dashboard)/_state/pendidikan/bimbingan-belajar-desa';
|
import stateBimbinganBelajarDesa from '@/app/admin/(dashboard)/_state/pendidikan/bimbingan-belajar-desa';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Paper, SimpleGrid, Skeleton, Stack, Text, Title, Tooltip, Divider, Badge, Group } from '@mantine/core';
|
import { Box, Divider, Group, Paper, SimpleGrid, Skeleton, Stack, Text, Title, Tooltip } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconBook2, IconCalendarTime, IconMapPin } from '@tabler/icons-react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import { IconMapPin, IconCalendarTime, IconBook2 } from '@tabler/icons-react';
|
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
@@ -55,9 +55,9 @@ function Page() {
|
|||||||
<IconBook2 size={36} stroke={1.5} color={colors['blue-button']} />
|
<IconBook2 size={36} stroke={1.5} color={colors['blue-button']} />
|
||||||
</Box>
|
</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Badge variant="gradient" gradient={{ from: colors['blue-button'], to: 'cyan' }} size="lg" radius="sm" mb="sm">
|
<Title order={3} fw={700} c={colors['blue-button']} mb="xs">
|
||||||
{stateTujuanProgram.findById.data?.judul}
|
{stateTujuanProgram.findById.data?.judul}
|
||||||
</Badge>
|
</Title>
|
||||||
</Group>
|
</Group>
|
||||||
<Text fz="md" style={{wordBreak: "break-word", whiteSpace: "normal"}} lh={1.6} dangerouslySetInnerHTML={{ __html: stateTujuanProgram.findById.data?.deskripsi }} />
|
<Text fz="md" style={{wordBreak: "break-word", whiteSpace: "normal"}} lh={1.6} dangerouslySetInnerHTML={{ __html: stateTujuanProgram.findById.data?.deskripsi }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -70,9 +70,9 @@ function Page() {
|
|||||||
<IconMapPin size={36} stroke={1.5} color={colors['blue-button']} />
|
<IconMapPin size={36} stroke={1.5} color={colors['blue-button']} />
|
||||||
</Box>
|
</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Badge variant="gradient" gradient={{ from: colors['blue-button'], to: 'cyan' }} size="lg" radius="sm" mb="sm">
|
<Title order={3} fw={700} c={colors['blue-button']} mb="xs">
|
||||||
{stateLokasiDanJadwal.findById.data?.judul}
|
{stateLokasiDanJadwal.findById.data?.judul}
|
||||||
</Badge>
|
</Title>
|
||||||
</Group>
|
</Group>
|
||||||
<Text fz="md" lh={1.6} style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: stateLokasiDanJadwal.findById.data?.deskripsi }} />
|
<Text fz="md" lh={1.6} style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: stateLokasiDanJadwal.findById.data?.deskripsi }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -85,9 +85,9 @@ function Page() {
|
|||||||
<IconCalendarTime size={36} stroke={1.5} color={colors['blue-button']} />
|
<IconCalendarTime size={36} stroke={1.5} color={colors['blue-button']} />
|
||||||
</Box>
|
</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Badge variant="gradient" gradient={{ from: colors['blue-button'], to: 'cyan' }} size="lg" radius="sm" mb="sm">
|
<Title order={3} fw={700} c={colors['blue-button']} mb="xs">
|
||||||
{stateFasilitas.findById.data?.judul}
|
{stateFasilitas.findById.data?.judul}
|
||||||
</Badge>
|
</Title>
|
||||||
</Group>
|
</Group>
|
||||||
<Text fz="md" lh={1.6} style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: stateFasilitas.findById.data?.deskripsi }} />
|
<Text fz="md" lh={1.6} style={{wordBreak: "break-word", whiteSpace: "normal"}} dangerouslySetInnerHTML={{ __html: stateFasilitas.findById.data?.deskripsi }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client';
|
'use client';
|
||||||
import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud';
|
import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud';
|
||||||
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Center, Container, Group, Paper, SimpleGrid, Skeleton, Stack, Text, Tooltip, ActionIcon } from '@mantine/core';
|
import { Box, Button, Center, Container, Group, Paper, SimpleGrid, Skeleton, Stack, Text, Tooltip, ActionIcon } from '@mantine/core';
|
||||||
import { IconChalkboard, IconMicroscope, IconProps, IconRefresh, IconSchool, IconInfoCircle } from '@tabler/icons-react';
|
import { IconChalkboard, IconMicroscope, IconProps, IconRefresh, IconSchool, IconInfoCircle } from '@tabler/icons-react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
@@ -119,11 +120,15 @@ export default function KategoriPage({ jenjangPendidikan }: { jenjangPendidikan:
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconRefresh size={16} />}
|
leftSection={<IconRefresh color={colors['blue-button']} size={16} />}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={handleRefresh}
|
onClick={handleRefresh}
|
||||||
loading={stats.some(stat => stat.loading)}
|
loading={stats.some(stat => stat.loading)}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
style={{
|
||||||
|
borderColor: colors['blue-button'],
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Segarkan Data
|
Segarkan Data
|
||||||
</Button>
|
</Button>
|
||||||
@@ -143,7 +148,7 @@ export default function KategoriPage({ jenjangPendidikan }: { jenjangPendidikan:
|
|||||||
aria-label="Tidak ada hasil"
|
aria-label="Tidak ada hasil"
|
||||||
>
|
>
|
||||||
<Center style={{ minHeight: 180, flexDirection: 'column' }}>
|
<Center style={{ minHeight: 180, flexDirection: 'column' }}>
|
||||||
<Text fz="lg" fw={800} c="#2563eb">
|
<Text fz="lg" fw={800} c={colors['blue-button']}>
|
||||||
Tidak ditemukan
|
Tidak ditemukan
|
||||||
</Text>
|
</Text>
|
||||||
<Text c="dimmed" mt="6px">
|
<Text c="dimmed" mt="6px">
|
||||||
@@ -201,7 +206,7 @@ export default function KategoriPage({ jenjangPendidikan }: { jenjangPendidikan:
|
|||||||
aria-hidden
|
aria-hidden
|
||||||
>
|
>
|
||||||
{React.createElement(v.icon, {
|
{React.createElement(v.icon, {
|
||||||
color: '#2563eb',
|
color: colors['blue-button'],
|
||||||
size: 34,
|
size: 34,
|
||||||
stroke: 1.6,
|
stroke: 1.6,
|
||||||
})}
|
})}
|
||||||
@@ -214,12 +219,12 @@ export default function KategoriPage({ jenjangPendidikan }: { jenjangPendidikan:
|
|||||||
{v.jumlah.toLocaleString()}
|
{v.jumlah.toLocaleString()}
|
||||||
</Text>
|
</Text>
|
||||||
<Group gap={6} align="center">
|
<Group gap={6} align="center">
|
||||||
<Text ta={"center"} fz="sm" fw={700} c="#2563eb">
|
<Text ta={"center"} fz="sm" fw={700} c={colors['blue-button']}>
|
||||||
{v.nama}
|
{v.nama}
|
||||||
</Text>
|
</Text>
|
||||||
<Tooltip label={v.helper ?? ''} position="right" withArrow>
|
<Tooltip label={v.helper ?? ''} position="right" withArrow>
|
||||||
<ActionIcon aria-label={`Info ${v.nama}`} variant="transparent" size="xs">
|
<ActionIcon aria-label={`Info ${v.nama}`} variant="transparent" size="xs">
|
||||||
<IconInfoCircle size={16} style={{ color: '#2563eb' }} />
|
<IconInfoCircle size={16} style={{ color: colors['blue-button'] }} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -233,8 +238,8 @@ export default function KategoriPage({ jenjangPendidikan }: { jenjangPendidikan:
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
aria-label={`Lihat detail ${v.nama}`}
|
aria-label={`Lihat detail ${v.nama}`}
|
||||||
style={{
|
style={{
|
||||||
borderColor: '#e2e8f0',
|
borderColor: colors['blue-button'],
|
||||||
color: '#2563eb',
|
color: colors['blue-button'],
|
||||||
paddingLeft: 20,
|
paddingLeft: 20,
|
||||||
paddingRight: 20,
|
paddingRight: 20,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ function Page({ params }: PageProps) {
|
|||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh w="30%">Nama Pengajar</TableTh>
|
<TableTh w="30%">Nama Pengajar</TableTh>
|
||||||
<TableTh w="30%">Nama Lembaga</TableTh>
|
<TableTh w="30%">Nama Lembaga</TableTh>
|
||||||
<TableTh w="40%">Jenjang Pendidikan</TableTh>
|
<TableTh w="40%">Mengajar Di Jenjang Pendidikan</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
|
|||||||
@@ -185,6 +185,8 @@ export default function LayoutSekolah({
|
|||||||
radius="xl"
|
radius="xl"
|
||||||
size="sm"
|
size="sm"
|
||||||
variant={aktif ? 'filled' : 'light'}
|
variant={aktif ? 'filled' : 'light'}
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
c={aktif ? colors['white-1'] : 'gray'}
|
||||||
>
|
>
|
||||||
{k}
|
{k}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud';
|
import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud';
|
||||||
|
import colors from '@/con/colors';
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Box,
|
Box,
|
||||||
@@ -130,11 +131,15 @@ export default function SekolahPage() {
|
|||||||
<Box>
|
<Box>
|
||||||
<Group justify="start" mb="md">
|
<Group justify="start" mb="md">
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconRefresh size={16} />}
|
leftSection={<IconRefresh color={colors['blue-button']} size={16} />}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={handleRefresh}
|
onClick={handleRefresh}
|
||||||
loading={stats.some(stat => stat.loading)}
|
loading={stats.some(stat => stat.loading)}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
style={{
|
||||||
|
borderColor: colors['blue-button'],
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Segarkan Data
|
Segarkan Data
|
||||||
</Button>
|
</Button>
|
||||||
@@ -154,7 +159,7 @@ export default function SekolahPage() {
|
|||||||
aria-label="Tidak ada hasil"
|
aria-label="Tidak ada hasil"
|
||||||
>
|
>
|
||||||
<Center style={{ minHeight: 180, flexDirection: 'column' }}>
|
<Center style={{ minHeight: 180, flexDirection: 'column' }}>
|
||||||
<Text fz="lg" fw={800} c="#2563eb">
|
<Text fz="lg" fw={800} c={colors['blue-button']}>
|
||||||
Tidak ditemukan
|
Tidak ditemukan
|
||||||
</Text>
|
</Text>
|
||||||
<Text c="dimmed" mt="6px">
|
<Text c="dimmed" mt="6px">
|
||||||
@@ -212,7 +217,7 @@ export default function SekolahPage() {
|
|||||||
aria-hidden
|
aria-hidden
|
||||||
>
|
>
|
||||||
{React.createElement(v.icon, {
|
{React.createElement(v.icon, {
|
||||||
color: '#2563eb',
|
color: colors['blue-button'],
|
||||||
size: 34,
|
size: 34,
|
||||||
stroke: 1.6,
|
stroke: 1.6,
|
||||||
})}
|
})}
|
||||||
@@ -225,12 +230,12 @@ export default function SekolahPage() {
|
|||||||
{v.jumlah.toLocaleString()}
|
{v.jumlah.toLocaleString()}
|
||||||
</Text>
|
</Text>
|
||||||
<Group gap={6} align="center">
|
<Group gap={6} align="center">
|
||||||
<Text ta={"center"} fz="sm" fw={700} c="#2563eb">
|
<Text ta={"center"} fz="sm" fw={700} c={colors['blue-button']}>
|
||||||
{v.nama}
|
{v.nama}
|
||||||
</Text>
|
</Text>
|
||||||
<Tooltip label={v.helper ?? ''} position="right" withArrow>
|
<Tooltip label={v.helper ?? ''} position="right" withArrow>
|
||||||
<ActionIcon aria-label={`Info ${v.nama}`} variant="transparent" size="xs">
|
<ActionIcon aria-label={`Info ${v.nama}`} variant="transparent" size="xs">
|
||||||
<IconInfoCircle size={16} style={{ color: '#2563eb' }} />
|
<IconInfoCircle size={16} style={{ color: colors['blue-button'] }} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -244,8 +249,8 @@ export default function SekolahPage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
aria-label={`Lihat detail ${v.nama}`}
|
aria-label={`Lihat detail ${v.nama}`}
|
||||||
style={{
|
style={{
|
||||||
borderColor: '#e2e8f0',
|
borderColor: colors['blue-button'],
|
||||||
color: '#2563eb',
|
color: colors['blue-button'],
|
||||||
paddingLeft: 20,
|
paddingLeft: 20,
|
||||||
paddingRight: 20,
|
paddingRight: 20,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -68,16 +68,16 @@ function Page() {
|
|||||||
<Image src="/darmasaba-icon.png" w={{ base: 70, md: 100 }} alt="Logo Desa Darmasaba" loading="lazy" />
|
<Image src="/darmasaba-icon.png" w={{ base: 70, md: 100 }} alt="Logo Desa Darmasaba" loading="lazy" />
|
||||||
</Center>
|
</Center>
|
||||||
<Text ta="center" fz={{ base: "1.8rem", md: "2.5rem" }} c={colors["blue-button"]} fw="bold" lh={1.4}>
|
<Text ta="center" fz={{ base: "1.8rem", md: "2.5rem" }} c={colors["blue-button"]} fw="bold" lh={1.4}>
|
||||||
Daftar Informasi Publik Desa Darmasaba
|
Daftar Informasi Publik
|
||||||
</Text>
|
</Text>
|
||||||
<Box px={{ base: "md", md: 100 }}>
|
<Box px={{ base: "md", md: 100 }}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Paper p="lg" radius="xl" shadow="sm" withBorder>
|
<Paper p="lg" radius="xl" shadow="sm" withBorder>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Text fz={{ base: 'lg', md: 'xl' }} fw="bold" c={colors["blue-button"]}>
|
<Text ta={"center"} fz={{ base: 'lg', md: 'xl' }} fw="bold" c={colors["blue-button"]}>
|
||||||
Tentang Informasi Publik
|
Tentang Informasi Publik
|
||||||
</Text>
|
</Text>
|
||||||
<Text fz={{ base: 'sm', md: 'md' }} c="dimmed">
|
<Text ta={"center"} fz={{ base: 'sm', md: 'md' }} c="dimmed">
|
||||||
Daftar Informasi Publik Desa Darmasaba adalah kumpulan data yang dapat diakses oleh masyarakat sesuai dengan ketentuan peraturan yang berlaku.
|
Daftar Informasi Publik Desa Darmasaba adalah kumpulan data yang dapat diakses oleh masyarakat sesuai dengan ketentuan peraturan yang berlaku.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -117,28 +117,32 @@ function Page() {
|
|||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd ta="center">{(page - 1) * 5 + index + 1}</TableTd>
|
<TableTd ta="center">{(page - 1) * 5 + index + 1}</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box w={150}>
|
<Box>
|
||||||
<Badge variant="light" size="lg" color="blue">
|
<Badge variant="light" size="lg" color="blue">
|
||||||
|
<Text fw={650} fz={"sm"} c={'blue'} lineClamp={1}>
|
||||||
{item.jenisInformasi}
|
{item.jenisInformasi}
|
||||||
|
</Text>
|
||||||
</Badge>
|
</Badge>
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box w={150}>
|
<Box>
|
||||||
<Text lineClamp={1} fz="sm" c="dark" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
<Text lineClamp={1} fz="sm" c="dark" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd ta="center">
|
<TableTd ta="center">
|
||||||
<Box w={150}>
|
<Box>
|
||||||
|
<Text ta={"center"}>
|
||||||
{item.tanggal ? new Date(item.tanggal).toLocaleDateString('id-ID', {
|
{item.tanggal ? new Date(item.tanggal).toLocaleDateString('id-ID', {
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
year: 'numeric'
|
year: 'numeric'
|
||||||
}) : '-'}
|
}) : '-'}
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd style={{ textAlign: 'center' }}>
|
<TableTd style={{ textAlign: 'center' }}>
|
||||||
<Box w={150}>
|
<Box>
|
||||||
<Tooltip label="Lihat Detail" withArrow>
|
<Tooltip label="Lihat Detail" withArrow>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -42,7 +42,7 @@ function Page() {
|
|||||||
>
|
>
|
||||||
Dasar Hukum
|
Dasar Hukum
|
||||||
</Text>
|
</Text>
|
||||||
<Text ta="center" fz="sm" c="dimmed">
|
<Text ta="center" fz="md" >
|
||||||
Informasi regulasi dan kebijakan resmi yang menjadi dasar hukum
|
Informasi regulasi dan kebijakan resmi yang menjadi dasar hukum
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{/* Chart Rating */}
|
{/* Chart Rating */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Pilihan</Title>
|
<Title order={4}>Ulasan</Title>
|
||||||
{donutDataRating.every(item => item.value === 0) ? (
|
{donutDataRating.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md">
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
@@ -505,7 +505,7 @@ const state = useProxy(indeksKepuasanState.responden);
|
|||||||
{/* Chart Rating */}
|
{/* Chart Rating */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Pilihan</Title>
|
<Title order={4}>Ulasan</Title>
|
||||||
{donutDataRating.every(item => item.value === 0) ? (
|
{donutDataRating.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md">
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
@@ -63,19 +63,25 @@ function Page() {
|
|||||||
<Box px={{ base: 0, md: 50 }} pb={40}>
|
<Box px={{ base: 0, md: 50 }} pb={40}>
|
||||||
<SimpleGrid cols={{ base: 1, xl: 2 }} spacing="xl">
|
<SimpleGrid cols={{ base: 1, xl: 2 }} spacing="xl">
|
||||||
<Box px={{ base: 0, md: 50 }}>
|
<Box px={{ base: 0, md: 50 }}>
|
||||||
<Paper bg={colors['white-trans-1']} radius="lg" shadow="sm">
|
<Paper bg={colors['white-trans-1']} radius="xl" shadow="md" withBorder>
|
||||||
<Stack gap="md">
|
<Stack gap={0}>
|
||||||
<Center>
|
|
||||||
<Image
|
<Image
|
||||||
loading='lazy'
|
pt={{ base: 0, md: 100 }}
|
||||||
|
px="lg"
|
||||||
src={item.image?.link ? `${item.image.link}?t=${Date.now()}` : "/perbekel.png"}
|
src={item.image?.link ? `${item.image.link}?t=${Date.now()}` : "/perbekel.png"}
|
||||||
w={{ base: 220, md: 330 }}
|
|
||||||
alt="Foto Pimpinan"
|
alt="Foto Pimpinan"
|
||||||
radius="md"
|
radius="lg"
|
||||||
|
onError={(e) => e.currentTarget.src = "/perbekel.png"}
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</Center>
|
<Paper
|
||||||
<Paper bg={colors['blue-button']} py={25} radius="lg" className="glass3">
|
bg={colors['blue-button']}
|
||||||
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "1.5rem", md: "2rem" }}>
|
px="lg"
|
||||||
|
radius="0 0 var(--mantine-radius-xl) var(--mantine-radius-xl)"
|
||||||
|
className="glass3"
|
||||||
|
py={{ base: 20, md: 50 }}
|
||||||
|
>
|
||||||
|
<Text ta="center" c={colors['white-1']} fw="bolder" fz={{ base: "xl", md: "h2" }}>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ function DetailPegawaiUser() {
|
|||||||
const data = statePegawai.findUnique.data;
|
const data = statePegawai.findUnique.data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="xl">
|
<Box px={{ base: 'md', md: 100 }} py="xl">
|
||||||
{/* Back button */}
|
{/* Back button */}
|
||||||
<Group mb="lg">
|
<Group mb="lg" px={{ base: 'md', md: 100 }}>
|
||||||
<Box
|
<Box
|
||||||
onClick={() => router.back()}
|
onClick={() => router.back()}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID'
|
import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID'
|
||||||
|
import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton'
|
||||||
import colors from '@/con/colors'
|
import colors from '@/con/colors'
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Center,
|
Center,
|
||||||
Container,
|
|
||||||
Group,
|
Group,
|
||||||
Image,
|
Image,
|
||||||
Loader,
|
Loader,
|
||||||
@@ -18,25 +17,24 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
Tooltip,
|
Transition
|
||||||
Transition,
|
|
||||||
} from '@mantine/core'
|
} from '@mantine/core'
|
||||||
import {
|
import {
|
||||||
|
IconArrowsMaximize,
|
||||||
|
IconArrowsMinimize,
|
||||||
IconRefresh,
|
IconRefresh,
|
||||||
IconSearch,
|
IconSearch,
|
||||||
IconUsers,
|
IconUsers,
|
||||||
IconZoomIn,
|
IconZoomIn,
|
||||||
IconZoomOut,
|
IconZoomOut,
|
||||||
IconArrowsMaximize,
|
|
||||||
IconArrowsMinimize,
|
|
||||||
} from '@tabler/icons-react'
|
} from '@tabler/icons-react'
|
||||||
|
import { debounce } from 'lodash'
|
||||||
|
import { useTransitionRouter } from 'next-view-transitions'
|
||||||
import { OrganizationChart } from 'primereact/organizationchart'
|
import { OrganizationChart } from 'primereact/organizationchart'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useProxy } from 'valtio/utils'
|
import { useProxy } from 'valtio/utils'
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto'
|
import BackButton from '../../desa/layanan/_com/BackButto'
|
||||||
import { useTransitionRouter } from 'next-view-transitions'
|
import './struktur.css'
|
||||||
import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton'
|
|
||||||
import { debounce } from 'lodash'
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
@@ -48,10 +46,9 @@ export default function Page() {
|
|||||||
paddingBottom: 48,
|
paddingBottom: 48,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container size="xl" py="xl">
|
<Box px={{ base: 'md', md: 100 }} py={"xl"}>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
|
||||||
<BackButton />
|
<BackButton />
|
||||||
</Box>
|
|
||||||
<Stack align="center" gap="xl" mt="xl">
|
<Stack align="center" gap="xl" mt="xl">
|
||||||
<Title
|
<Title
|
||||||
order={1}
|
order={1}
|
||||||
@@ -66,12 +63,12 @@ export default function Page() {
|
|||||||
untuk melihat detail atau klik node untuk fokus tampilan.
|
untuk melihat detail atau klik node untuk fokus tampilan.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Box mt="lg">
|
<Box mt="lg">
|
||||||
<StrukturOrganisasiPPID />
|
<StrukturOrganisasiPPID />
|
||||||
</Box>
|
</Box>
|
||||||
</Container>
|
</Box>
|
||||||
|
|
||||||
{/* Tombol Scroll ke Atas */}
|
|
||||||
<ScrollToTopButton />
|
<ScrollToTopButton />
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
@@ -85,7 +82,7 @@ function StrukturOrganisasiPPID() {
|
|||||||
const [isFullscreen, setFullscreen] = useState(false)
|
const [isFullscreen, setFullscreen] = useState(false)
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
|
|
||||||
// debounce untuk pencarian
|
// debounce pencarian
|
||||||
const debouncedSearch = useRef(
|
const debouncedSearch = useRef(
|
||||||
debounce((value: string) => {
|
debounce((value: string) => {
|
||||||
setSearchQuery(value)
|
setSearchQuery(value)
|
||||||
@@ -113,7 +110,8 @@ function StrukturOrganisasiPPID() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stateOrganisasi.findMany.data || stateOrganisasi.findMany.data.length === 0) {
|
const data = stateOrganisasi.findMany.data || []
|
||||||
|
if (data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Center py={40}>
|
<Center py={40}>
|
||||||
<Stack align="center" gap="md">
|
<Stack align="center" gap="md">
|
||||||
@@ -152,9 +150,9 @@ function StrukturOrganisasiPPID() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buat struktur organisasi
|
// 🧩 buat struktur organisasi
|
||||||
const posisiMap = new Map<string, any>()
|
const posisiMap = new Map<string, any>()
|
||||||
const aktifPegawai = stateOrganisasi.findMany.data.filter((p: any) => p.isActive)
|
const aktifPegawai = data.filter((p: any) => p.isActive)
|
||||||
|
|
||||||
for (const pegawai of aktifPegawai) {
|
for (const pegawai of aktifPegawai) {
|
||||||
const posisiId = pegawai.posisi.id
|
const posisiId = pegawai.posisi.id
|
||||||
@@ -177,19 +175,15 @@ function StrukturOrganisasiPPID() {
|
|||||||
} else root.push(posisi)
|
} else root.push(posisi)
|
||||||
})
|
})
|
||||||
|
|
||||||
function toOrgChartFormat(node: any): any {
|
const toOrgChartFormat = (node: any): any => {
|
||||||
const pegawai = node.pegawaiList?.[0]
|
const pegawai = node.pegawaiList?.[0]
|
||||||
return {
|
return {
|
||||||
expanded: true,
|
expanded: true,
|
||||||
type: 'person',
|
|
||||||
styleClass: 'p-person',
|
|
||||||
data: {
|
data: {
|
||||||
id: pegawai?.id || null,
|
id: pegawai?.id,
|
||||||
name: pegawai?.namaLengkap || 'Belum ditugaskan',
|
name: pegawai?.namaLengkap || 'Belum Ditugaskan',
|
||||||
title: node.nama || 'Tanpa jabatan',
|
title: node.nama || 'Tanpa Jabatan',
|
||||||
image: pegawai?.image?.link || '/img/default.png',
|
image: pegawai?.image?.link || '/img/default.png',
|
||||||
description: node.deskripsi || '',
|
|
||||||
positionId: node.id || null,
|
|
||||||
},
|
},
|
||||||
children: node.children?.map(toOrgChartFormat) || [],
|
children: node.children?.map(toOrgChartFormat) || [],
|
||||||
}
|
}
|
||||||
@@ -214,7 +208,7 @@ function StrukturOrganisasiPPID() {
|
|||||||
chartData = filterNodes(chartData)
|
chartData = filterNodes(chartData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🧭 fungsi fullscreen
|
// 🎬 fullscreen & zoom control
|
||||||
const toggleFullscreen = () => {
|
const toggleFullscreen = () => {
|
||||||
if (!document.fullscreenElement) {
|
if (!document.fullscreenElement) {
|
||||||
chartContainerRef.current?.requestFullscreen()
|
chartContainerRef.current?.requestFullscreen()
|
||||||
@@ -225,140 +219,249 @@ function StrukturOrganisasiPPID() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🧭 fungsi zoom
|
const handleZoomIn = () => setScale((s) => Math.min(s + 0.1, 2))
|
||||||
const handleZoomIn = () => setScale((prev) => Math.min(prev + 0.1, 2))
|
const handleZoomOut = () => setScale((s) => Math.max(s - 0.1, 0.5))
|
||||||
const handleZoomOut = () => setScale((prev) => Math.max(prev - 0.1, 0.5))
|
|
||||||
const resetZoom = () => setScale(1)
|
const resetZoom = () => setScale(1)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack align="center" mt="xl">
|
<Stack align="center" mt="xl">
|
||||||
{/* 🔍 Search + Zoom + Fullscreen controls */}
|
{/* 🔍 Controls */}
|
||||||
<Group mb="md" justify="center" gap="sm" align="center">
|
<Paper
|
||||||
|
shadow="xs"
|
||||||
|
p="md"
|
||||||
|
radius="md"
|
||||||
|
style={{
|
||||||
|
background: colors['blue-button']
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Group gap="sm" wrap="wrap" justify="center">
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Cari nama atau jabatan..."
|
placeholder="Cari nama atau jabatan..."
|
||||||
leftSection={<IconSearch size={16} />}
|
leftSection={<IconSearch size={16} />}
|
||||||
onChange={(e) => debouncedSearch(e.target.value)}
|
onChange={(e) => debouncedSearch(e.target.value)}
|
||||||
|
styles={{
|
||||||
|
input: {
|
||||||
|
minWidth: 250,
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button variant="light" size="sm" onClick={handleZoomOut}>
|
<Group gap="xs">
|
||||||
<IconZoomOut size={16} />
|
<Button
|
||||||
|
variant="light"
|
||||||
|
bg={colors['blue-button-2']}
|
||||||
|
size="sm"
|
||||||
|
onClick={handleZoomOut}
|
||||||
|
leftSection={<IconZoomOut size={16} />}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Zoom Out
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* 🔍 Tambahkan indikator zoom di sini */}
|
|
||||||
{/* Floating Zoom Indicator */}
|
|
||||||
<Box
|
<Box
|
||||||
bg="#C3D0E8"
|
bg={colors['blue-button-2']}
|
||||||
c="blue"
|
c={colors['blue-button']}
|
||||||
px={9}
|
px={16}
|
||||||
py={8}
|
py={8}
|
||||||
style={{
|
style={{
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: 600,
|
fontWeight: 700,
|
||||||
borderRadius: '5px',
|
borderRadius: '8px',
|
||||||
|
minWidth: 70,
|
||||||
|
textAlign: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Math.round(scale * 100)}%
|
{Math.round(scale * 100)}%
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Button
|
||||||
<Button variant="light" size="sm" onClick={handleZoomIn}>
|
bg={colors['blue-button-2']}
|
||||||
<IconZoomIn size={16} />
|
c={colors['blue-button']}
|
||||||
|
variant="light"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleZoomIn}
|
||||||
|
leftSection={<IconZoomIn size={16} />}
|
||||||
|
>
|
||||||
|
Zoom In
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button variant="light" size="sm" onClick={resetZoom}>
|
<Button
|
||||||
|
bg={colors['blue-button-2']}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
variant="light"
|
||||||
|
size="sm"
|
||||||
|
onClick={resetZoom}
|
||||||
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
bg={colors['blue-button-2']}
|
||||||
|
c={colors['blue-button']}
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={toggleFullscreen}
|
onClick={toggleFullscreen}
|
||||||
leftSection={
|
leftSection={
|
||||||
isFullscreen ? <IconArrowsMinimize size={16} /> : <IconArrowsMaximize size={16} />
|
isFullscreen ? (
|
||||||
|
<IconArrowsMinimize size={16} />
|
||||||
|
) : (
|
||||||
|
<IconArrowsMaximize size={16} />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{isFullscreen ? 'Keluar' : 'Fullscreen'}
|
Fullscreen
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* 🧩 Chart Container */}
|
||||||
{/* Chart Container */}
|
<Center style={{ width: '100%' }}>
|
||||||
<Box
|
<Box
|
||||||
ref={chartContainerRef}
|
ref={chartContainerRef}
|
||||||
style={{
|
style={{
|
||||||
overflow: 'auto',
|
overflowX: 'auto',
|
||||||
|
overflowY: 'auto',
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: '100%',
|
||||||
|
padding: '32px 16px',
|
||||||
|
transition: 'transform 0.2s ease',
|
||||||
transform: `scale(${scale})`,
|
transform: `scale(${scale})`,
|
||||||
transformOrigin: 'center top',
|
transformOrigin: 'center top',
|
||||||
transition: 'transform 0.25s ease',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<OrganizationChart
|
<OrganizationChart
|
||||||
value={chartData}
|
value={chartData}
|
||||||
nodeTemplate={(node) => nodeTemplate(node, router)}
|
nodeTemplate={(node) => <NodeCard node={node} router={router} />}
|
||||||
|
className="p-organizationchart p-organizationchart-horizontal"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Center>
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeTemplate(node: any, router: ReturnType<typeof useTransitionRouter>) {
|
function NodeCard({ node, router }: any) {
|
||||||
const imageSrc = node?.data?.image || '/img/default.png'
|
const imageSrc = node?.data?.image || '/img/default.png'
|
||||||
const name = node?.data?.name || 'Tanpa Nama'
|
const name = node?.data?.name || 'Tanpa Nama'
|
||||||
const title = node?.data?.title || 'Tanpa Jabatan'
|
const title = node?.data?.title || 'Tanpa Jabatan'
|
||||||
const description = node?.data?.description || ''
|
const hasId = Boolean(node?.data?.id)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition mounted transition="pop" duration={240}>
|
<Transition mounted transition="pop" duration={300}>
|
||||||
{(styles) => (
|
{(styles) => (
|
||||||
<Card
|
<Card
|
||||||
radius="lg"
|
shadow="md"
|
||||||
|
radius="xl"
|
||||||
withBorder
|
withBorder
|
||||||
style={{
|
style={{
|
||||||
...styles,
|
...styles,
|
||||||
width: 260,
|
width: 240,
|
||||||
padding: 16,
|
minHeight: 280,
|
||||||
background: 'rgba(28,110,164,0.3)',
|
padding: 20,
|
||||||
borderColor: 'rgba(255,255,255,0.15)',
|
background: 'linear-gradient(135deg, rgba(28,110,164,0.15) 0%, rgba(255,255,255,0.95) 100%)',
|
||||||
display: 'flex',
|
borderColor: 'rgba(28, 110, 164, 0.3)',
|
||||||
flexDirection: 'column',
|
borderWidth: 2,
|
||||||
alignItems: 'center',
|
transition: 'all 0.3s ease',
|
||||||
textAlign: 'center',
|
cursor: hasId ? 'pointer' : 'default',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
if (hasId) {
|
||||||
|
e.currentTarget.style.transform = 'translateY(-4px)'
|
||||||
|
e.currentTarget.style.boxShadow = '0 8px 24px rgba(28, 110, 164, 0.25)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
if (hasId) {
|
||||||
|
e.currentTarget.style.transform = 'translateY(0)'
|
||||||
|
e.currentTarget.style.boxShadow = ''
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack align="center" gap={12}>
|
||||||
|
{/* Photo */}
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
width: 96,
|
||||||
|
height: 96,
|
||||||
|
borderRadius: '50%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
border: '3px solid rgba(28, 110, 164, 0.4)',
|
||||||
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
|
||||||
|
background: 'white',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={imageSrc}
|
src={imageSrc}
|
||||||
alt={name}
|
alt={name}
|
||||||
radius="md"
|
width={96}
|
||||||
width={60}
|
height={96}
|
||||||
height={60}
|
|
||||||
fit="cover"
|
fit="cover"
|
||||||
|
loading="lazy"
|
||||||
style={{
|
style={{
|
||||||
objectFit: 'cover',
|
objectFit: 'cover',
|
||||||
border: '2px solid rgba(255,255,255,0.2)',
|
|
||||||
marginBottom: 12,
|
|
||||||
}}
|
}}
|
||||||
loading="lazy"
|
|
||||||
/>
|
/>
|
||||||
<Text fw={700}>{name}</Text>
|
</Box>
|
||||||
<Text size="sm" c="dimmed" mt={4}>
|
|
||||||
|
{/* Name */}
|
||||||
|
<Text
|
||||||
|
fw={700}
|
||||||
|
size="sm"
|
||||||
|
ta="center"
|
||||||
|
c={colors['blue-button']}
|
||||||
|
lineClamp={2}
|
||||||
|
style={{
|
||||||
|
minHeight: 40,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
lineHeight: 1.3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{/* Title/Position */}
|
||||||
|
<Text
|
||||||
|
size="xs"
|
||||||
|
c="dimmed"
|
||||||
|
ta="center"
|
||||||
|
fw={500}
|
||||||
|
lineClamp={2}
|
||||||
|
style={{
|
||||||
|
minHeight: 32,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
lineHeight: 1.2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs" c="dimmed" mt={8} lineClamp={3}>
|
|
||||||
{description || 'Belum ada deskripsi.'}
|
{/* Detail Button */}
|
||||||
</Text>
|
{hasId && (
|
||||||
<Tooltip label="Lihat Detail" withArrow position="bottom">
|
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="gradient"
|
||||||
|
gradient={{ from: 'blue', to: 'cyan' }}
|
||||||
size="xs"
|
size="xs"
|
||||||
mt="md"
|
fullWidth
|
||||||
onClick={() => {
|
mt={8}
|
||||||
const id = node?.data?.id
|
radius="md"
|
||||||
router.push(`/darmasaba/ppid/struktur-ppid/${id}`)
|
onClick={() =>
|
||||||
|
router.push(`/darmasaba/ppid/struktur-ppid/${node.data.id}`)
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
height: 32,
|
||||||
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Lihat Detail
|
Lihat Detail
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
)}
|
||||||
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|||||||
68
src/app/darmasaba/(pages)/ppid/struktur-ppid/struktur.css
Normal file
68
src/app/darmasaba/(pages)/ppid/struktur-ppid/struktur.css
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/* ============================================
|
||||||
|
STRUKTUR ORGANISASI PPID - STYLING
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
/* Tabel chart selalu center */
|
||||||
|
.p-organizationchart-table {
|
||||||
|
margin: 0 auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Jarak vertikal antar level - lebih lega */
|
||||||
|
.p-organizationchart-line-down {
|
||||||
|
height: 32px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Padding di dalam node - lebih rapi */
|
||||||
|
.p-organizationchart-node-content {
|
||||||
|
padding: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Garis connector antar node - lebih tebal dan jelas */
|
||||||
|
.p-organizationchart-line-down,
|
||||||
|
.p-organizationchart-line-left,
|
||||||
|
.p-organizationchart-line-right,
|
||||||
|
.p-organizationchart-line-top {
|
||||||
|
border-color: rgba(28, 110, 164, 0.4) !important;
|
||||||
|
border-width: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Garis horizontal */
|
||||||
|
.p-organizationchart-line-left,
|
||||||
|
.p-organizationchart-line-right {
|
||||||
|
border-top-width: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Jarak horizontal antar node - lebih proporsional */
|
||||||
|
.p-organizationchart-table > tbody > tr > td {
|
||||||
|
padding: 0 24px !important;
|
||||||
|
vertical-align: top !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Node container spacing */
|
||||||
|
.p-organizationchart-node {
|
||||||
|
padding: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.p-organizationchart-table > tbody > tr > td {
|
||||||
|
padding: 0 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-organizationchart-line-down {
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smooth transitions untuk zoom */
|
||||||
|
.p-organizationchart {
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fullscreen mode adjustments */
|
||||||
|
.p-organizationchart-table:fullscreen {
|
||||||
|
background: rgba(230, 240, 255, 0.98);
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
@@ -54,12 +54,11 @@ function Page() {
|
|||||||
ta="center"
|
ta="center"
|
||||||
fz={{ base: 28, md: 36 }}
|
fz={{ base: 28, md: 36 }}
|
||||||
fw={800}
|
fw={800}
|
||||||
variant="gradient"
|
c={colors['blue-button']}
|
||||||
gradient={{ from: colors['blue-button'], to: 'cyan', deg: 45 }}
|
|
||||||
>
|
>
|
||||||
Moto PPID Desa Darmasaba
|
Moto PPID Desa Darmasaba
|
||||||
</Text>
|
</Text>
|
||||||
<Text ta="center" fz={{ base: 16, md: 20 }} mt="xs" c="dimmed">
|
<Text ta="center" fz={{ base: 16, md: 20 }} mt="xs">
|
||||||
Memberikan informasi yang cepat, mudah, tepat, dan transparan
|
Memberikan informasi yang cepat, mudah, tepat, dan transparan
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -67,7 +66,8 @@ function Page() {
|
|||||||
<Divider my="sm" labelPosition="center" label={<IconSparkles size={18} />} />
|
<Divider my="sm" labelPosition="center" label={<IconSparkles size={18} />} />
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text ta="center" fz={{ base: 24, md: 30 }} fw={700} mb="sm">
|
<Text ta="center" fz={{ base: 24, md: 30 }} fw={800}
|
||||||
|
c={colors['blue-button']} mb="sm">
|
||||||
Visi PPID
|
Visi PPID
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
@@ -82,7 +82,8 @@ function Page() {
|
|||||||
<Divider my="sm" />
|
<Divider my="sm" />
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text ta="center" fz={{ base: 24, md: 30 }} fw={700} mb="sm">
|
<Text ta="center" fz={{ base: 24, md: 30 }} fw={800}
|
||||||
|
c={colors['blue-button']} mb="sm">
|
||||||
Misi PPID
|
Misi PPID
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { ActionIcon, Anchor, Box, Button, Center, Container, Divider, Flex, Group, Image, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
|
import { ActionIcon, Anchor, Box, Button, Center, Container, Divider, Flex, Group, Image, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
import { IconAt, IconBrandFacebook, IconBrandInstagram, IconBrandTiktok, IconBrandYoutube } from '@tabler/icons-react';
|
import { IconAt, IconBrandFacebook, IconBrandInstagram, IconBrandTiktok, IconBrandYoutube } from '@tabler/icons-react';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
const sosialMedia = [
|
const sosialMedia = [
|
||||||
{
|
{
|
||||||
@@ -60,6 +62,39 @@ const tautanPenting = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
function Footer() {
|
function Footer() {
|
||||||
|
|
||||||
|
const emailRef = useRef<HTMLInputElement>(null)
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const email = emailRef.current?.value.trim();
|
||||||
|
if (!email) return toast.error('Email wajib diisi!');
|
||||||
|
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
if (!emailRegex.test(email)) return toast.error('Format email tidak valid!');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/subscribe', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ email }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (res.ok && data.success) {
|
||||||
|
toast.success('Berhasil! Cek email Anda untuk konfirmasi.');
|
||||||
|
emailRef.current!.value = '';
|
||||||
|
} else {
|
||||||
|
toast.error(data.message || 'Gagal berlangganan.');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
toast.error('Gagal menghubungi server. Coba lagi nanti.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack bg="linear-gradient(180deg, #1C6EA4, #124170)" c="white">
|
<Stack bg="linear-gradient(180deg, #1C6EA4, #124170)" c="white">
|
||||||
<Box w="100%" p="xl">
|
<Box w="100%" p="xl">
|
||||||
@@ -166,8 +201,9 @@ function Footer() {
|
|||||||
w="70%"
|
w="70%"
|
||||||
placeholder="Masukkan email Anda"
|
placeholder="Masukkan email Anda"
|
||||||
rightSection={<IconAt size={16} />}
|
rightSection={<IconAt size={16} />}
|
||||||
|
ref={emailRef} // ini aja cukup
|
||||||
/>
|
/>
|
||||||
<Button variant="gradient" gradient={{ from: 'blue', to: 'cyan' }} radius="md">Daftar</Button>
|
<Button onClick={handleSubmit} variant="gradient" gradient={{ from: 'blue', to: 'cyan' }} radius="md">Daftar</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ function NavbarMobile({ listNavbar }: { listNavbar: MenuItem[] }) {
|
|||||||
radius="md"
|
radius="md"
|
||||||
p="sm"
|
p="sm"
|
||||||
withBorder
|
withBorder
|
||||||
bg={active ? "blue.0" : "gray.0"}
|
bg={active ? colors["blue-button-2"] : "gray.0"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (item.href) {
|
if (item.href) {
|
||||||
router.push(item.href);
|
router.push(item.href);
|
||||||
@@ -126,21 +126,21 @@ function NavbarMobile({ listNavbar }: { listNavbar: MenuItem[] }) {
|
|||||||
style={{
|
style={{
|
||||||
cursor: item.href ? "pointer" : "default",
|
cursor: item.href ? "pointer" : "default",
|
||||||
transition: "background 0.15s ease",
|
transition: "background 0.15s ease",
|
||||||
borderLeft: active ? "4px solid #1e66f5" : "4px solid transparent",
|
borderLeft: active ? `4px solid ${colors['blue-button']}` : "4px solid transparent",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Group justify="space-between" align="center" wrap="nowrap">
|
<Group justify="space-between" align="center" wrap="nowrap">
|
||||||
<Text
|
<Text
|
||||||
fw={active ? 700 : 600}
|
fw={active ? 700 : 600}
|
||||||
fz="md"
|
fz="md"
|
||||||
c={active ? "blue.7" : "dark.9"}
|
c={active ? colors['blue-button'] : "dark.9"}
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
{item.href && (
|
{item.href && (
|
||||||
<IconSquareArrowRight
|
<IconSquareArrowRight
|
||||||
size={18}
|
size={18}
|
||||||
color={active ? "#1e66f5" : "inherit"}
|
color={active ? colors['blue-button'] : "inherit"}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
@@ -167,21 +167,21 @@ function NavbarMobile({ listNavbar }: { listNavbar: MenuItem[] }) {
|
|||||||
cursor: child.href ? "pointer" : "default",
|
cursor: child.href ? "pointer" : "default",
|
||||||
opacity: child.href ? 1 : 0.8,
|
opacity: child.href ? 1 : 0.8,
|
||||||
borderRadius: "0.5rem",
|
borderRadius: "0.5rem",
|
||||||
backgroundColor: childActive ? "#e7f0ff" : "transparent",
|
backgroundColor: childActive ? colors["blue-button-2"] : "transparent",
|
||||||
borderLeft: childActive ? "3px solid #1e66f5" : "3px solid transparent",
|
borderLeft: childActive ? `3px solid ${colors['blue-button']}` : "3px solid transparent",
|
||||||
transition: "background 0.15s ease",
|
transition: "background 0.15s ease",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
fz="sm"
|
fz="sm"
|
||||||
fw={childActive ? 600 : 400}
|
fw={childActive ? 600 : 400}
|
||||||
c={childActive ? "blue.7" : "dark.8"}
|
c={childActive ? colors['blue-button'] : "dark.8"}
|
||||||
>
|
>
|
||||||
{child.name}
|
{child.name}
|
||||||
</Text>
|
</Text>
|
||||||
<IconSquareArrowRight
|
<IconSquareArrowRight
|
||||||
size={14}
|
size={14}
|
||||||
color={childActive ? "#1e66f5" : "inherit"}
|
color={childActive ? colors['blue-button'] : "inherit"}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ function MenuItemCom({ item, isActive = false }: { item: MenuItem, isActive?: bo
|
|||||||
<MenuTarget>
|
<MenuTarget>
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color={isActive ? 'blue' : 'gray'}
|
color={isActive ? colors['blue-button'] : 'gray'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (item.href) {
|
if (item.href) {
|
||||||
router.push(item.href);
|
router.push(item.href);
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ export function NavbarSubMenu({ item }: { item: MenuItem[] | null }) {
|
|||||||
rightSection={<IconArrowRight size={18} />}
|
rightSection={<IconArrowRight size={18} />}
|
||||||
styles={(theme) => ({
|
styles={(theme) => ({
|
||||||
root: {
|
root: {
|
||||||
background: link.href && pathname.startsWith(link.href) ? theme.colors.blue[0] : 'transparent',
|
background: link.href && pathname.startsWith(link.href) ? colors['blue-button-2'] : 'transparent',
|
||||||
color: link.href && pathname.startsWith(link.href) ? theme.colors.blue[7] : colors['blue-button'],
|
color: link.href && pathname.startsWith(link.href) ? colors['blue-button'] : 'gray',
|
||||||
fontWeight: link.href && pathname.startsWith(link.href) ? 600 : 500,
|
fontWeight: link.href && pathname.startsWith(link.href) ? 600 : 500,
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
background: link.href && pathname.startsWith(link.href) ? theme.colors.blue[1] : theme.colors.gray[0],
|
background: link.href && pathname.startsWith(link.href) ? colors['blue-button-2'] : theme.colors.gray[0],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ function Apbdes() {
|
|||||||
const data = (state.findMany.data || []).slice(0, 3)
|
const data = (state.findMany.data || []).slice(0, 3)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p="lg" gap="4rem" bg={colors.Bg}>
|
<Stack p="sm" gap="xl" bg={colors.Bg}>
|
||||||
<Box>
|
<Box>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Text ta={"center"} fw={"bold"} fz={{ base: "1.8rem", md: "3.4rem" }}>
|
<Text c={colors["blue-button"]} ta={"center"} fw={"bold"} fz={{ base: "1.8rem", md: "3.4rem" }}>
|
||||||
{textHeading.title}
|
{textHeading.title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>
|
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>
|
||||||
@@ -117,7 +117,7 @@ function Apbdes() {
|
|||||||
)}
|
)}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
|
||||||
<Group pb={80} justify="center">
|
<Group justify="center" pb={10}>
|
||||||
<Button
|
<Button
|
||||||
component={Link}
|
component={Link}
|
||||||
href="/darmasaba/apbdes"
|
href="/darmasaba/apbdes"
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ function DesaAntiKorupsi() {
|
|||||||
const data = (state.desaAntikorupsi.findMany.data || []).slice(0, 6);
|
const data = (state.desaAntikorupsi.findMany.data || []).slice(0, 6);
|
||||||
return (
|
return (
|
||||||
<Stack gap={"0"} bg={colors.Bg} p={"sm"}>
|
<Stack gap={"0"} bg={colors.Bg} p={"sm"}>
|
||||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"} >
|
<Container w={{ base: "100%", md: "80%" }} p={"md"} >
|
||||||
<Center>
|
<Center>
|
||||||
<Text fw={"bold"} fz={{ base: "1.8rem", md: "3.4rem" }}>Desa Anti Korupsi</Text>
|
<Text fw={"bold"} c={colors["blue-button"]} fz={{ base: "1.8rem", md: "3.4rem" }}>Desa Anti Korupsi</Text>
|
||||||
</Center>
|
</Center>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>Desa antikorupsi mendorong pemerintahan jujur dan transparan. Keuangan desa dikelola terbuka dengan melibatkan warga mengawasi anggaran, sehingga digunakan tepat sasaran sesuai kebutuhan.</Text>
|
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>Desa antikorupsi mendorong pemerintahan jujur dan transparan. Keuangan desa dikelola terbuka dengan melibatkan warga mengawasi anggaran, sehingga digunakan tepat sasaran sesuai kebutuhan.</Text>
|
||||||
<Center py={20}>
|
<Center py={20}>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ function Kepuasan() {
|
|||||||
|
|
||||||
if ((loading && !data) || !data) {
|
if ((loading && !data) || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10} px="xl">
|
<Stack py={10} px="sm">
|
||||||
<Skeleton height={300} mb="md" />
|
<Skeleton height={300} mb="md" />
|
||||||
<SimpleGrid cols={{ base: 1, md: 3 }}>
|
<SimpleGrid cols={{ base: 1, md: 3 }}>
|
||||||
<Skeleton height={300} />
|
<Skeleton height={300} />
|
||||||
@@ -154,9 +154,15 @@ function Kepuasan() {
|
|||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Stack p="sm">
|
<Stack p="sm">
|
||||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"}>
|
<Container w={{ base: "100%", md: "80%" }} p={"sm"}>
|
||||||
<Center>
|
<Center>
|
||||||
<Text fw={"bold"} fz={{ base: "1.8rem", md: "3.4rem" }}>Indeks Kepuasan Masyarakat</Text>
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: '2rem', md: '2.8rem' }}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
fw={800}
|
||||||
|
style={{ letterSpacing: '-0.5px' }}
|
||||||
|
>Indeks Kepuasan Masyarakat</Text>
|
||||||
</Center>
|
</Center>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!</Text>
|
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!</Text>
|
||||||
<Center mt={10}>
|
<Center mt={10}>
|
||||||
@@ -168,7 +174,7 @@ function Kepuasan() {
|
|||||||
>Ajukan Responden</Button>
|
>Ajukan Responden</Button>
|
||||||
</Center>
|
</Center>
|
||||||
</Container>
|
</Container>
|
||||||
<Box px={"xl"}>
|
<Box px={"sm"}>
|
||||||
<Paper p={"lg"} bg={colors.Bg}>
|
<Paper p={"lg"} bg={colors.Bg}>
|
||||||
<Paper p={"lg"}>
|
<Paper p={"lg"}>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
@@ -239,7 +245,7 @@ function Kepuasan() {
|
|||||||
{/* Chart Rating */}
|
{/* Chart Rating */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Pilihan</Title>
|
<Title order={4}>Ulasan</Title>
|
||||||
{donutDataRating.every(item => item.value === 0) ? (
|
{donutDataRating.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md">
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
@@ -416,16 +422,22 @@ function Kepuasan() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Stack p={"sm"}>
|
<Stack p={"sm"}>
|
||||||
<Container size="lg" px="md">
|
<Container size="lg" px="sm">
|
||||||
<Center>
|
<Center>
|
||||||
<Text ta={"center"} fz={{ base: "2.4rem", md: "3.4rem" }}>Indeks Kepuasan Masyarakat</Text>
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: '2rem', md: '2.8rem' }}
|
||||||
|
c={colors['blue-button']}
|
||||||
|
fw={800}
|
||||||
|
style={{ letterSpacing: '-0.5px' }}
|
||||||
|
>Indeks Kepuasan Masyarakat</Text>
|
||||||
</Center>
|
</Center>
|
||||||
<Text fz={{ base: "1.2rem", md: "1.4rem" }} ta={"center"}>Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!</Text>
|
<Text fz={{ base: "1.2rem", md: "1.4rem" }} ta={"center"}>Ukur kebahagiaan warga, tingkatkan layanan desa! Dengan partisipasi aktif masyarakat, kami berkomitmen untuk terus memperbaiki layanan agar lebih transparan, efektif, dan sesuai dengan kebutuhan warga. Kepuasan Anda adalah prioritas utama kami dalam membangun desa yang lebih baik!</Text>
|
||||||
<Center mt={10}>
|
<Center mt={10}>
|
||||||
<Button radius={"lg"} bg={colors["blue-button"]} onClick={open}>Ajukan Responden</Button>
|
<Button radius={"lg"} bg={colors["blue-button"]} onClick={open}>Ajukan Responden</Button>
|
||||||
</Center>
|
</Center>
|
||||||
</Container>
|
</Container>
|
||||||
<Box px={"xl"}>
|
<Box px={"md"}>
|
||||||
<Paper p={"lg"} bg={colors.Bg}>
|
<Paper p={"lg"} bg={colors.Bg}>
|
||||||
<Paper p={"lg"}>
|
<Paper p={"lg"}>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
@@ -505,7 +517,7 @@ function Kepuasan() {
|
|||||||
{/* Chart Rating */}
|
{/* Chart Rating */}
|
||||||
<Paper bg={colors['white-1']} p="md" radius="md">
|
<Paper bg={colors['white-1']} p="md" radius="md">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Pilihan</Title>
|
<Title order={4}>Ulasan</Title>
|
||||||
{donutDataRating.every(item => item.value === 0) ? (
|
{donutDataRating.every(item => item.value === 0) ? (
|
||||||
<Text c="dimmed" ta="center" my="md">
|
<Text c="dimmed" ta="center" my="md">
|
||||||
Belum ada data untuk ditampilkan dalam grafik
|
Belum ada data untuk ditampilkan dalam grafik
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ function LandingPage() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack bg={colors.Bg} p="md" gap="xl">
|
<Stack bg={colors.Bg} p="md" gap="lg">
|
||||||
<Flex gap="lg" wrap={{ base: "wrap", md: "nowrap" }}>
|
<Flex gap="lg" wrap={{ base: "wrap", md: "nowrap" }}>
|
||||||
<Stack w={{ base: "100%", md: "65%" }} gap="lg">
|
<Stack w={{ base: "100%", md: "65%" }} gap="lg">
|
||||||
<Card radius="xl" bg={colors.grey[1]} p="lg" shadow="xl">
|
<Card radius="xl" bg={colors.grey[1]} p="lg" shadow="xl">
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ const textHeading = {
|
|||||||
function Layanan() {
|
function Layanan() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pos={"relative"} bg={colors.grey[1]} gap={"42"} py={"xl"}>
|
<Stack pos={"relative"} bg={colors.grey[1]} gap={"xl"} py={"md"}>
|
||||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"} >
|
<Container w={{ base: "100%", md: "80%" }} p={"md"} >
|
||||||
<Stack align="center" gap={"0"}>
|
<Stack align="center" gap={"0"}>
|
||||||
<Text fw={"bold"} fz={{ base: "1.8rem", md: "3.4rem" }}>
|
<Text fw={"bold"} c={colors["blue-button"]} fz={{ base: "1.8rem", md: "3.4rem" }}>
|
||||||
{textHeading.title}
|
{textHeading.title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>
|
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ function Potensi() {
|
|||||||
const data = (state.findMany.data || []).slice(0, 4);
|
const data = (state.findMany.data || []).slice(0, 4);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack p="sm" gap="4rem">
|
<Stack p="sm" gap="xl">
|
||||||
<Container w={{ base: "100%", md: "80%" }} p={"xl"} >
|
<Container w={{ base: "100%", md: "80%" }} p={"md"} >
|
||||||
<Text ta={"center"} fw={"bold"} fz={{ base: "1.8rem", md: "3.4rem" }}>
|
<Text ta={"center"} fw={"bold"} c={colors["blue-button"]} fz={{ base: "1.8rem", md: "3.4rem" }}>
|
||||||
{textHeading.title}
|
{textHeading.title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>
|
<Text ta={"center"} fz={{ base: "1rem", md: "1.3rem" }}>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function Prestasi() {
|
|||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<IconTrophy size={36} color={colors["blue-button"]} />
|
<IconTrophy size={36} color={colors["blue-button"]} />
|
||||||
<Text ta="center" fz={{ base: "2rem", md: "3.4rem" }} fw={700}>
|
<Text c={colors["blue-button"]} ta="center" fz={{ base: "2rem", md: "3.4rem" }} fw={700}>
|
||||||
Prestasi Desa
|
Prestasi Desa
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -55,7 +55,7 @@ function Prestasi() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
<Box py={50}>
|
<Box py="lg">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Center mih={200}>
|
<Center mih={200}>
|
||||||
<Loader color={colors["blue-button"]} size="xl" />
|
<Loader color={colors["blue-button"]} size="xl" />
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useMediaQuery } from "@mantine/hooks"
|
|||||||
import { Prisma } from "@prisma/client"
|
import { Prisma } from "@prisma/client"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { IconMoodSad } from "@tabler/icons-react"
|
import { IconMoodSad } from "@tabler/icons-react"
|
||||||
|
import colors from "@/con/colors"
|
||||||
|
|
||||||
export default function SDGS() {
|
export default function SDGS() {
|
||||||
const theme = useMantineTheme()
|
const theme = useMantineTheme()
|
||||||
@@ -41,11 +42,7 @@ export default function SDGS() {
|
|||||||
order={1}
|
order={1}
|
||||||
fz={{ base: "2.4rem", md: "3.6rem" }}
|
fz={{ base: "2.4rem", md: "3.6rem" }}
|
||||||
fw={900}
|
fw={900}
|
||||||
style={{
|
c={colors["blue-button"]}
|
||||||
background: "linear-gradient(90deg, #1A5F7A, #159895)",
|
|
||||||
WebkitBackgroundClip: "text",
|
|
||||||
WebkitTextFillColor: "transparent",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
SDGs Desa
|
SDGs Desa
|
||||||
</Title>
|
</Title>
|
||||||
@@ -54,7 +51,7 @@ export default function SDGS() {
|
|||||||
SDGs Desa merupakan langkah nyata untuk mewujudkan desa yang maju, inklusif, dan berkelanjutan melalui 17 tujuan pembangunan dari pengentasan kemiskinan, pendidikan, kesehatan, kesetaraan gender, hingga pelestarian lingkungan.
|
SDGs Desa merupakan langkah nyata untuk mewujudkan desa yang maju, inklusif, dan berkelanjutan melalui 17 tujuan pembangunan dari pengentasan kemiskinan, pendidikan, kesehatan, kesetaraan gender, hingga pelestarian lingkungan.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Box py={50}>
|
<Box py="lg">
|
||||||
<Paper
|
<Paper
|
||||||
p={{ base: "md", md: "xl" }}
|
p={{ base: "md", md: "xl" }}
|
||||||
radius="2xl"
|
radius="2xl"
|
||||||
@@ -156,7 +153,7 @@ export default function SDGS() {
|
|||||||
href="/darmasaba/sdgs-desa"
|
href="/darmasaba/sdgs-desa"
|
||||||
radius="xl"
|
radius="xl"
|
||||||
size="lg"
|
size="lg"
|
||||||
mt={40}
|
mt="md"
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
gradient={{ from: "#26667F", to: "#124170" }}
|
gradient={{ from: "#26667F", to: "#124170" }}
|
||||||
style={{ boxShadow: "0 6px 14px rgba(18,65,112,0.25)"}}
|
style={{ boxShadow: "0 6px 14px rgba(18,65,112,0.25)"}}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ import ScrollToTopButton from "./_com/scrollToTopButton";
|
|||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Stack bg={colors.grey[1]} gap={"4rem"}>
|
<Stack
|
||||||
|
bg={colors.grey[1]}
|
||||||
|
gap={"1.5rem"}
|
||||||
|
>
|
||||||
<LandingPage />
|
<LandingPage />
|
||||||
<Penghargaan />
|
<Penghargaan />
|
||||||
<Layanan />
|
<Layanan />
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
const colors = {
|
const colors = {
|
||||||
"orange": "#FCAE00",
|
"orange": "#FCAE00",
|
||||||
"blue-button": "#0A4E78",
|
"blue-button": "#0A4E78",
|
||||||
|
"blue-button-1": "#E5F2FA",
|
||||||
|
"blue-button-2": "#B8DAEF",
|
||||||
|
"blue-button-3": "#8AC1E3",
|
||||||
|
"blue-button-4": "#5DA9D8",
|
||||||
|
"blue-button-5": "#2F91CC",
|
||||||
|
"blue-button-6": "#083F61",
|
||||||
|
"blue-button-7": "#062F49",
|
||||||
|
"blue-button-8": "#041F32",
|
||||||
"blue-button-trans": "#628EC6",
|
"blue-button-trans": "#628EC6",
|
||||||
"white-1": "#FBFBFC",
|
"white-1": "#FBFBFC",
|
||||||
"white-trans-1": "rgba(255, 255, 255, 0.5)",
|
"white-trans-1": "rgba(255, 255, 255, 0.5)",
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ const navbarListMenu = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "1.7",
|
id: "1.7",
|
||||||
name: "Daftar Informasi Publik Desa Darmasaba",
|
name: "Daftar Informasi Publik",
|
||||||
href: "/darmasaba/ppid/daftar-informasi-publik-desa-darmasaba"
|
href: "/darmasaba/ppid/daftar-informasi-publik"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "1.8",
|
id: "1.8",
|
||||||
name: "IKM Desa Darmasaba",
|
name: "Indeks Kepuasan Masyarakat",
|
||||||
href: "/darmasaba/ppid/ikm-desa-darmasaba"
|
href: "/darmasaba/ppid/indeks-kepuasan-masyarakat"
|
||||||
},
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user