Files
desa-darmasaba/src/app/darmasaba/page.tsx
nico 0a8a026b94 fix(apbdes): integrate new APBDes API with admin UI
- Update API schema to support name, deskripsi, and jumlah fields
- Enhance state management with additional form fields
- Add input fields for name, description, and total amount in create/edit pages
- Display description and total amount in detail page
- Fix APBDes component order in landing page
- Update TypeScript types and Prisma schema integration

API Changes:
- POST /api/landingpage/apbdes/create: Added optional fields (name, deskripsi, jumlah)
- PUT /api/landingpage/apbdes/🆔 Added optional fields (name, deskripsi, jumlah)

Admin UI Changes:
- create/page.tsx: Add TextInput for name, deskripsi, and jumlah
- edit/page.tsx: Add TextInput for name, deskripsi, and jumlah; improve reset functionality
- [id]/page.tsx: Display deskripsi and jumlah if available
- page.tsx: Minor formatting fix
- _state/apbdes.ts: Update Zod schema and default form with new fields

Landing Page:
- Move Apbdes component to top of stack for better visibility

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-03-03 10:56:30 +08:00

175 lines
6.1 KiB
TypeScript

/* eslint-disable react-hooks/exhaustive-deps */
'use client';
import DesaAntiKorupsi from "@/app/darmasaba/_com/main-page/desaantikorupsi";
import Kepuasan from "@/app/darmasaba/_com/main-page/kepuasan";
import LandingPage from "@/app/darmasaba/_com/main-page/landing-page";
import Layanan from "@/app/darmasaba/_com/main-page/layanan";
import Penghargaan from "@/app/darmasaba/_com/main-page/penghargaan";
import Potensi from "@/app/darmasaba/_com/main-page/potensi";
import colors from "@/con/colors";
import SDGS from "./_com/main-page/sdgs";
import { Box, Stack } from "@mantine/core";
import Apbdes from "./_com/main-page/apbdes";
import Prestasi from "./_com/main-page/prestasi";
import ScrollToTopButton from "./_com/scrollToTopButton";
import { useEffect, useRef, useState } from "react";
import { useSnapshot } from "valtio";
import stateDashboardBerita from "../admin/(dashboard)/_state/desa/berita";
import stateDesaPengumuman from "../admin/(dashboard)/_state/desa/pengumuman";
import NewsReaderLanding from "./_com/NewsReaderalanding";
import ModernNewsNotification from "./_com/ModernNewsNotification";
import type { NewsItem } from "./_com/ModernNewsNotification"; // pastikan tipe ini diekspor
export default function Page() {
// Tetap gunakan Valtio untuk card utama (NewsReaderLanding)
const snap1 = useSnapshot(stateDashboardBerita.berita.findFirst);
const snap2 = useSnapshot(stateDesaPengumuman.pengumuman.findFirst);
const featured = snap1;
const pengumuman = snap2;
const loadingFeatured = featured.loading;
const loadingPengumuman = pengumuman.loading;
// State untuk notifikasi
const [notificationNews, setNotificationNews] = useState<NewsItem[]>([]);
const [hasNewContent, setHasNewContent] = useState(false);
const [newItemCount, setNewItemCount] = useState(0);
const lastBeritaTimestamp = useRef<string | null>(null);
const lastPengumumanTimestamp = useRef<string | null>(null);
// Inisialisasi dari localStorage
useEffect(() => {
const savedBeritaTs = localStorage.getItem("lastSeenBeritaTs");
const savedPengumumanTs = localStorage.getItem("lastSeenPengumumanTs");
if (savedBeritaTs) lastBeritaTimestamp.current = savedBeritaTs;
if (savedPengumumanTs) lastPengumumanTimestamp.current = savedPengumumanTs;
}, []);
// Load data utama (untuk card)
useEffect(() => {
if (!featured.data && !loadingFeatured) {
stateDashboardBerita.berita.findFirst.load();
}
}, []);
useEffect(() => {
if (!pengumuman.data && !loadingPengumuman) {
stateDesaPengumuman.pengumuman.findFirst.load();
}
}, []);
// 🔁 Fetch berita & pengumuman lengkap untuk notifikasi
const fetchNotificationData = async () => {
try {
const res = await fetch("/api/news/latest");
const result = await res.json();
if (result.success && Array.isArray(result.news)) {
const news = result.news as NewsItem[];
const latestBerita = news.find((n) => n.type === "berita");
const latestPengumuman = news.find((n) => n.type === "pengumuman");
const latestBeritaTs = latestBerita?.timestamp
? new Date(latestBerita.timestamp).toISOString()
: null;
const latestPengumumanTs = latestPengumuman?.timestamp
? new Date(latestPengumuman.timestamp).toISOString()
: null;
// Inisialisasi flag
let isNewBerita = false;
let isNewPengumuman = false;
// Deteksi berita baru
if (latestBeritaTs) {
if (lastBeritaTimestamp.current === null) {
// Pertama kali: simpan tanpa notifikasi
lastBeritaTimestamp.current = latestBeritaTs;
localStorage.setItem("lastSeenBeritaTs", latestBeritaTs);
} else if (latestBeritaTs > lastBeritaTimestamp.current) {
isNewBerita = true;
lastBeritaTimestamp.current = latestBeritaTs;
}
}
// Deteksi pengumuman baru
if (latestPengumumanTs) {
if (lastPengumumanTimestamp.current === null) {
// Pertama kali: simpan tanpa notifikasi
lastPengumumanTimestamp.current = latestPengumumanTs;
localStorage.setItem("lastSeenPengumumanTs", latestPengumumanTs);
} else if (latestPengumumanTs > lastPengumumanTimestamp.current) {
isNewPengumuman = true;
lastPengumumanTimestamp.current = latestPengumumanTs;
}
}
// 🔔 Trigger notifikasi hanya jika ada yang benar-benar BARU
if (isNewBerita || isNewPengumuman) {
const count = (isNewBerita ? 1 : 0) + (isNewPengumuman ? 1 : 0);
setNewItemCount(count);
setHasNewContent(true); // ✅ INI YANG KAMU LUPA!
}
setNotificationNews(news);
}
} catch (err) {
console.error("Gagal fetch data notifikasi:", err);
}
};
// Load data notifikasi pertama kali
useEffect(() => {
fetchNotificationData();
}, []);
// Polling setiap 30 detik
useEffect(() => {
const interval = setInterval(fetchNotificationData, 30_000);
return () => clearInterval(interval);
}, []);
const handleSeen = () => {
setHasNewContent(false);
setNewItemCount(0);
const latestBerita = notificationNews.find(n => n.type === "berita");
const latestPengumuman = notificationNews.find(n => n.type === "pengumuman");
if (latestBerita) {
localStorage.setItem("lastSeenBeritaTs", new Date(latestBerita.timestamp!).toISOString());
}
if (latestPengumuman) {
localStorage.setItem("lastSeenPengumumanTs", new Date(latestPengumuman.timestamp!).toISOString());
}
};
return (
<Box id="page-root">
<Stack bg={colors.grey[1]} gap={0}>
<LandingPage />
<Apbdes />
<Penghargaan />
<Layanan />
<Potensi />
<DesaAntiKorupsi />
<Kepuasan />
<SDGS />
<Prestasi />
<ScrollToTopButton />
<NewsReaderLanding />
<ModernNewsNotification
news={notificationNews}
hasNewContent={hasNewContent}
newItemCount={newItemCount}
onSeen={handleSeen}
autoShowDelay={2000}
/>
</Stack>
</Box>
);
}