Fix undefined ke detail berita terbaru

This commit is contained in:
2025-12-05 17:42:04 +08:00
parent ec3ad12531
commit dcb8017594
6 changed files with 191 additions and 173 deletions

View File

@@ -15,15 +15,17 @@ import Apbdes from "./_com/main-page/apbdes";
import Prestasi from "./_com/main-page/prestasi";
import ScrollToTopButton from "./_com/scrollToTopButton";
import { useEffect, useMemo, useRef, useState } from "react";
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;
@@ -31,13 +33,15 @@ export default function Page() {
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 lastBeritaId = useRef<string | null>(null);
const lastPengumumanId = useRef<string | null>(null);
// 🔁 Inisialisasi dari localStorage saat mount
// Inisialisasi dari localStorage
useEffect(() => {
const savedBerita = localStorage.getItem("lastSeenBeritaId");
const savedPengumuman = localStorage.getItem("lastSeenPengumumanId");
@@ -45,13 +49,7 @@ export default function Page() {
if (savedPengumuman) lastPengumumanId.current = savedPengumuman;
}, []);
// Simpan ID saat data dimuat (termasuk dari API)
useEffect(() => {
if (featured.data?.id) lastBeritaId.current = featured.data.id;
if (pengumuman.data?.id) lastPengumumanId.current = pengumuman.data.id;
}, [featured.data?.id, pengumuman.data?.id]);
// Load data awal
// Load data utama (untuk card)
useEffect(() => {
if (!featured.data && !loadingFeatured) {
stateDashboardBerita.berita.findFirst.load();
@@ -64,91 +62,64 @@ export default function Page() {
}
}, []);
// 🔁 Polling untuk cek update setiap 30 detik
useEffect(() => {
const checkForUpdates = async () => {
try {
const res = await fetch("/api/check-update");
const result = await res.json();
// 🔁 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[];
if (!result.success) return;
// Ambil ID terbaru
const latestBerita = news.find((n) => n.type === "berita");
const latestPengumuman = news.find((n) => n.type === "pengumuman");
const { berita, pengumuman } = result.data;
const isNewBerita = latestBerita && lastBeritaId.current !== null && latestBerita.id !== lastBeritaId.current;
const isNewPengumuman = latestPengumuman && lastPengumumanId.current !== null && latestPengumuman.id !== lastPengumumanId.current;
// Deteksi hanya jika sudah pernah ada data sebelumnya
const isNewBerita = berita && lastBeritaId.current !== null && berita.id !== lastBeritaId.current;
const isNewPengumuman = pengumuman && lastPengumumanId.current !== null && pengumuman.id !== lastPengumumanId.current;
// Simpan ID terbaru ke ref
if (latestBerita) lastBeritaId.current = (latestBerita.id);
if (latestPengumuman) lastPengumumanId.current = (latestPengumuman.id);
if (isNewBerita || isNewPengumuman) {
// Hitung berapa yang benar-benar baru
const count = (isNewBerita ? 1 : 0) + (isNewPengumuman ? 1 : 0);
setNewItemCount(count);
setHasNewContent(true);
// Reload hanya yang berubah
if (isNewBerita) stateDashboardBerita.berita.findFirst.load();
if (isNewPengumuman) stateDesaPengumuman.pengumuman.findFirst.load();
// Jika ini bukan inisialisasi pertama, tampilkan notifikasi
if (lastBeritaId.current !== null || lastPengumumanId.current !== null) {
if (isNewBerita || isNewPengumuman) {
const count = (isNewBerita ? 1 : 0) + (isNewPengumuman ? 1 : 0);
setNewItemCount(count);
setHasNewContent(true);
}
} else {
// Jika ini adalah pertama kali (masih null), simpan ID tanpa notifikasi
if (lastBeritaId.current === null && berita) {
lastBeritaId.current = berita.id;
localStorage.setItem("lastSeenBeritaId", berita.id);
}
if (lastPengumumanId.current === null && pengumuman) {
lastPengumumanId.current = pengumuman.id;
localStorage.setItem("lastSeenPengumumanId", pengumuman.id);
}
// Simpan ke localStorage saat pertama kali
if (latestBerita) localStorage.setItem("lastSeenBeritaId", (latestBerita.id));
if (latestPengumuman) localStorage.setItem("lastSeenPengumumanId", (latestPengumuman.id));
}
} catch (err) {
console.error("Gagal cek update berita/pengumuman:", err);
}
};
const interval = setInterval(checkForUpdates, 30_000);
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 newsData = useMemo(() => {
const items = [];
if (featured.data) {
items.push({
id: String(featured.data.id || "berita-1"),
type: "berita" as const,
title: String(featured.data.judul || "Berita Terbaru"),
content: String(featured.data.content || ""),
timestamp: featured.data.createdAt
? (typeof featured.data.createdAt === 'string'
? featured.data.createdAt
: new Date(featured.data.createdAt).toISOString())
: new Date().toISOString(),
});
}
if (pengumuman.data) {
items.push({
id: String(pengumuman.data.id || "pengumuman-1"),
type: "pengumuman" as const,
title: String(pengumuman.data.judul || "Pengumuman Penting"),
content: String(pengumuman.data.content || ""),
timestamp: pengumuman.data.createdAt
? (typeof pengumuman.data.createdAt === 'string'
? pengumuman.data.createdAt
: new Date(pengumuman.data.createdAt).toISOString())
: new Date().toISOString(),
});
}
return items;
}, [featured.data, pengumuman.data]);
const handleSeen = () => {
setHasNewContent(false);
setNewItemCount(0);
// Simpan ke localStorage saat dilihat
if (featured.data?.id) localStorage.setItem("lastSeenBeritaId", featured.data.id);
if (pengumuman.data?.id) localStorage.setItem("lastSeenPengumumanId", pengumuman.data.id);
};
setHasNewContent(false);
setNewItemCount(0);
const latestBerita = notificationNews.find(n => n.type === "berita");
const latestPengumuman = notificationNews.find(n => n.type === "pengumuman");
if (latestBerita) localStorage.setItem("lastSeenBeritaId", String(latestBerita.id));
if (latestPengumuman) localStorage.setItem("lastSeenPengumumanId", String(latestPengumuman.id));
};
return (
<Box id="page-root">
@@ -168,7 +139,7 @@ export default function Page() {
<NewsReaderLanding />
<ModernNewsNotification
news={newsData}
news={notificationNews}
hasNewContent={hasNewContent}
newItemCount={newItemCount}
onSeen={handleSeen}