Jum'at, 30 May 2025 :

Yang Sudah Di Kerjakan
* Tampilan UI Admin di menu inovasi
* API Create, edit dan delete potensi
* Tampilan UI Landing Page sudah sesuai di mobile

Yang Lagi Dikerjakan:
* Progress Tampilan UI Admin Di Menu lingkungan
* Progress API Create, edit dan delete potensi

Yang Akan Dikerjakan:
* API Create, edit dan delete pengumuman
* Tampilan UI Admin Di Menu Pendidikan
This commit is contained in:
2025-05-30 21:13:55 +08:00
parent 77f99a7c8f
commit 8f2b9665a9
12 changed files with 110 additions and 136 deletions

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import {
@@ -15,19 +14,19 @@ import {
Title,
} from "@mantine/core";
import { IconArrowBack, IconImageInPicture } from "@tabler/icons-react";
import { useParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { useRouter, useParams } from "next/navigation";
import { useProxy } from "valtio/utils";
import { toast } from "react-toastify";
import { useProxy } from "valtio/utils";
import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
import colors from "@/con/colors";
import ApiFetch from "@/lib/api-fetch";
import { FileInput } from "@mantine/core";
import stateDashboardBerita from "../../../../_state/desa/berita";
import { Prisma } from "@prisma/client";
import { useShallowEffect } from "@mantine/hooks";
import { BeritaEditor } from "../../_com/BeritaEditor";
import colors from "@/con/colors";
import { Prisma } from "@prisma/client";
import stateDashboardBerita from "../../../../_state/desa/berita";
function EditBerita() {
const beritaState = useProxy(stateDashboardBerita);
@@ -36,8 +35,6 @@ function EditBerita() {
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const [editorInstance, setEditorInstance] = useState<any>(null);
const [isEditorReady, setIsEditorReady] = useState(false);
const [formData, setFormData] = useState({
judul: beritaState.berita.edit.form.judul || '',
deskripsi: beritaState.berita.edit.form.deskripsi || '',
@@ -76,28 +73,7 @@ function EditBerita() {
loadBerita();
}, [params?.id]); // ✅ hapus beritaState dari dependency
// Handle editor ready
const handleEditorReady = (editor: any) => {
setEditorInstance(editor);
setIsEditorReady(true);
// Set initial content if exists
if (formData.content) {
editor.commands.setContent(formData.content);
}
};
const handleSubmit = async () => {
if (!isEditorReady || !editorInstance) {
return toast.error("Editor belum siap");
}
const htmlContent = editorInstance.getHTML();
if (!htmlContent || htmlContent === "<p></p>") {
return toast.warn("Konten tidak boleh kosong");
}
try {
// Update global state with form data
@@ -105,7 +81,7 @@ function EditBerita() {
...beritaState.berita.edit.form,
judul: formData.judul,
deskripsi: formData.deskripsi,
content: htmlContent,
content: formData.content,
kategoriBeritaId: formData.kategoriBeritaId || '',
imageId: formData.imageId // Keep existing imageId if not changed
};
@@ -189,13 +165,11 @@ function EditBerita() {
<Box>
<Text fz={"sm"} fw={"bold"}>Konten</Text>
<BeritaEditor
initialContent={formData.content}
onEditorReady={handleEditorReady}
showSubmit={false}
onUpdate={(content) => {
setFormData((prev) => ({ ...prev, content }));
beritaState.berita.edit.form.content = content;
<EditEditor
value={formData.content}
onChange={(htmlContent) => {
setFormData((prev) => ({ ...prev, content: htmlContent }));
beritaState.berita.edit.form.content = htmlContent;
}}
/>
</Box>

View File

@@ -8,8 +8,8 @@ import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import colors from '@/con/colors';
import { ModalKonfirmasiHapus } from '../../../../_com/modalKonfirmasiHapus';
import stateDashboardBerita from '../../../../_state/desa/berita';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import stateDashboardBerita from '../../../_state/desa/berita';
function DetailBerita() {
const beritaState = useProxy(stateDashboardBerita)
@@ -54,15 +54,27 @@ function DetailBerita() {
<Text fz={"xl"} fw={"bold"}>Detail Berita</Text>
{beritaState.berita.findUnique.data ? (
<Paper key={beritaState.berita.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fw={"bold"} fz={"lg"}>Kategori</Text>
<Text fz={"lg"}>{beritaState.berita.findUnique.data?.kategoriBerita?.name}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Judul</Text>
<Text fz={"lg"}>{beritaState.berita.findUnique.data?.judul}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
<Text fz={"lg"} >{beritaState.berita.findUnique.data?.deskripsi}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 150, lg: 150 }} src={beritaState.berita.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Konten</Text>
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: beritaState.berita.findUnique.data?.content }} />
</Box>
<Flex gap={"xs"} mt={10}>
<Button
onClick={() => {
@@ -79,7 +91,7 @@ function DetailBerita() {
<Button
onClick={() => {
if (beritaState.berita.findUnique.data) {
router.push(`/admin/desa/berita/edit/${beritaState.berita.findUnique.data.id}`);
router.push(`/admin/desa/berita/${beritaState.berita.findUnique.data.id}/edit`);
}
}}
disabled={!beritaState.berita.findUnique.data}
@@ -88,7 +100,7 @@ function DetailBerita() {
<IconEdit size={20} />
</Button>
</Flex>
</Box>
</Stack>
</Paper>
) : null}
</Stack>

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
@@ -10,14 +9,13 @@ import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
import CreateEditor from '../../../_com/createEditor';
import stateDashboardBerita from '../../../_state/desa/berita';
import { BeritaEditor } from '../_com/BeritaEditor';
export default function CreateBerita() {
const beritaState = useProxy(stateDashboardBerita);
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const [editorInstance, setEditorInstance] = useState<any>(null);
const router = useRouter()
const resetForm = () => {
@@ -33,21 +31,12 @@ export default function CreateBerita() {
// Reset state lokal
setPreviewImage(null);
setFile(null);
if (editorInstance) {
editorInstance.commands.setContent(""); // Kosongkan editor
}
};
const handleSubmit = async () => {
if (!file) {
return toast.warn("Pilih file gambar terlebih dahulu");
}
if (!editorInstance) return toast.error("Editor belum siap");
const htmlContent = editorInstance.getHTML();
if (!htmlContent || htmlContent === "<p></p>") return toast.warn("Konten tidak boleh kosong");
beritaState.berita.create.form.content = htmlContent;
// Upload gambar dulu
const res = await ApiFetch.api.fileStorage.create.post({
@@ -124,9 +113,11 @@ export default function CreateBerita() {
)}
<Box>
<Text fz={"sm"} fw={"bold"}>Konten</Text>
<BeritaEditor
showSubmit={false}
onEditorReady={(ed) => setEditorInstance(ed)}
<CreateEditor
value={beritaState.berita.create.form.content}
onChange={(htmlContent) => {
beritaState.berita.create.form.content = htmlContent;
}}
/>
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan Berita</Button>

View File

@@ -163,7 +163,7 @@ function BeritaList() {
<Image w={100} src={item.image?.link} alt="gambar" />
</TableTd>
<TableTd>
<Button bg={"green"} onClick={() => router.push(`/admin/desa/berita/detail/${item.id}`)}>
<Button bg={"green"} onClick={() => router.push(`/admin/desa/berita/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>

View File

@@ -13,9 +13,6 @@ type FormCreate = Prisma.BeritaGetPayload<{
}>;
async function beritaCreate(context: Context) {
const body = context.body as FormCreate;
console.log(body)
// console.log(body)
await prisma.berita.create({
data: {

View File

@@ -40,26 +40,20 @@ export default async function handler(
}
// Ensure we're returning a proper Response object
return new Response(JSON.stringify({
return Response.json({
success: true,
message: "Success fetch berita by ID",
data,
}), {
}, {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
} catch (e) {
console.error("Find by ID error:", e);
return new Response(JSON.stringify({
return Response.json({
success: false,
message: "Gagal mengambil berita: " + (e instanceof Error ? e.message : 'Unknown error'),
}), {
}, {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
}

View File

@@ -10,7 +10,7 @@ function Footer() {
return (
<>
<Stack bg={colors["blue-button"]}>
<Box w={mobile ? "100%" : "100%"} p={"xl"} h={{ base: 1850, md: 1100 }} >
<Box w={mobile ? "100%" : "100%"} p={"xl"} h={{ base: 2500, md: 1100 }} >
<Center>
<Paper w={"100%"}>
<Box component="footer" py="xl">

View File

@@ -44,9 +44,9 @@ function DesaAntiKorupsi() {
<Stack gap={"0"} bg={colors.Bg} p={"sm"} h={mobile ? 2000 : 1150}>
<Container w={{ base: "100%", md: "80%" }} p={"xl"} >
<Center>
<Text fz={"3.4rem"}>Desa Anti Korupsi</Text>
<Text fz={{base: "2.4rem", md: "3.4rem"}}>Desa Anti Korupsi</Text>
</Center>
<Text ta={"center"} fz={"1.4rem"}>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: "1.2rem", md: "1.4rem"}}>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}>
<Button radius={"lg"} fz={"h4"} bg={colors["blue-button"]} component={Link} href={"/darmasaba/desaantikorupsi"}>Selengkapnya</Button>
</Center>
@@ -65,13 +65,13 @@ function DesaAntiKorupsi() {
<Paper p={"lg"} >
<Flex gap={"lg"} justify={"center"} align={"center"}>
<Box >
<Text fz={"lg"} ta={"center"} c={colors["blue-button"]}>{v.judul}</Text>
<Text fz={{base: "1.2rem", md: "lg"}} ta={"center"} c={colors["blue-button"]}>{v.judul}</Text>
<Flex justify={"center"} align={"center"}>
<Box>
{v.icon}
</Box>
<Box px={20}>
<Text fz={"sm"} ta={"justify"}>{v.deskripsi}</Text>
<Text fz={"sm"} ta={{base: "left", md: "justify"}}>{v.deskripsi}</Text>
</Box>
</Flex>
</Box>

View File

@@ -1,7 +1,8 @@
"use client";
import { Stack, Container, Center, Text, Paper, Flex, Box, SimpleGrid } from "@mantine/core";
import { BarChart, PieChart } from '@mantine/charts';
import colors from "@/con/colors";
import { BarChart, PieChart } from '@mantine/charts';
import { Box, Center, Container, Flex, Paper, SimpleGrid, Stack, Text } from "@mantine/core";
import { useMediaQuery } from "@mantine/hooks";
const dataBarChart = [
{
@@ -71,13 +72,14 @@ const dataPieChart3 = [
]
function Kepuasan() {
const isMobile = useMediaQuery('(max-width: 768px)');
return (
<Stack p={"sm"}>
<Container w={{ base: "100%", md: "80%" }} p={"xl"}>
<Center>
<Text fz={"3.4rem"}>Indeks Kepuasan Masyarakat</Text>
<Text ta={"center"} fz={{base: "2.4rem", md: "3.4rem"}}>Indeks Kepuasan Masyarakat</Text>
</Center>
<Text fz={"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>
</Container>
<Box px={"xl"}>
<Paper p={"lg"} bg={colors.Bg}>
@@ -118,7 +120,7 @@ function Kepuasan() {
<Text fw={"bold"}>Jenis Kelamin</Text>
<Box py={"xl"}>
<PieChart
size={250}
size={isMobile ? 100 : 220}
withLabelsLine
labelsPosition="outside"
labelsType="percent"
@@ -135,7 +137,7 @@ function Kepuasan() {
<Text fw={"bold"}>Pilihan</Text>
<Box py={"xl"}>
<PieChart
size={250}
size={isMobile ? 100 : 220}
withLabelsLine
labelsPosition="outside"
labelsType="percent"
@@ -152,7 +154,7 @@ function Kepuasan() {
<Text fw={"bold"}>Umur</Text>
<Box py={"xl"}>
<PieChart
size={250}
size={isMobile ? 100 : 220}
withLabelsLine
labelsPosition="outside"
labelsType="percent"

View File

@@ -5,9 +5,9 @@ import {
Card,
Flex,
Grid,
GridCol,
Image,
Paper,
SimpleGrid,
Stack,
Text
} from "@mantine/core";
@@ -58,10 +58,9 @@ function LandingPage() {
<Grid
>
<Grid.Col span={{
base: 2,
sm: 3,
base: 3,
lg: 2,
md: 3,
xl: 2
}}>
<Box
pos={"relative"}
@@ -88,10 +87,9 @@ function LandingPage() {
</Grid.Col>
<Grid.Col span={{
base: 2,
sm: 3,
base: 6,
lg: 2,
md: 3,
xl: 2
}}>
<Box
pos={"relative"}
@@ -118,10 +116,9 @@ function LandingPage() {
</Box>
</Grid.Col>
<Grid.Col span={{
base: 8,
sm: 12,
base: 12,
lg: 8,
md: 12,
xl: 8
}}>
<Paper
pos={"relative"}
@@ -130,14 +127,13 @@ function LandingPage() {
w={{ base: "100%", sm: "auto", md: "auto" }}
flex={{ base: "1", sm: "1", md: "1" }}
>
<SimpleGrid
cols={{
base: 2,
sm: 1,
md: 2,
}}
spacing={{ base: "xs", md: "md" }}
<Grid
>
<GridCol span={{
base: 12,
lg: 6,
md: 6,
}}>
<Box>
<Text c={colors["white-1"]} fz={"sm"}>
Jadwal Kerja
@@ -168,6 +164,13 @@ function LandingPage() {
</Flex>
</Paper>
</Box>
</GridCol>
<GridCol span={{
base: 12,
lg: 6,
md: 6,
}}>
<Box>
<Text c={colors["white-1"]} fz={"sm"}>
Rabu, 10 Maret 2025
@@ -187,7 +190,8 @@ function LandingPage() {
</Box>
</Paper>
</Box>
</SimpleGrid>
</GridCol>
</Grid>
</Paper>
</Grid.Col>

View File

@@ -51,7 +51,7 @@ function Penghargaan() {
<Text fz={"1.4rem"} c={"white"}>
Juara 2 Duta Investasi
</Text>
<Text fz={"1.4rem"} c={"white"}>
<Text fz={"1.2rem"} c={"white"}>
Juara Favorit Lomba Video Pendek
</Text>
</Stack>