Fix undefined ke detail berita terbaru
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
// app/api/check-update/route.ts
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Ambil berita terbaru
|
||||
const latestBerita = await prisma.berita.findFirst({
|
||||
orderBy: { createdAt: "desc" },
|
||||
select: { id: true, createdAt: true },
|
||||
});
|
||||
|
||||
// Ambil pengumuman terbaru
|
||||
const latestPengumuman = await prisma.pengumuman.findFirst({
|
||||
orderBy: { createdAt: "desc" },
|
||||
select: { id: true, createdAt: true },
|
||||
});
|
||||
|
||||
return Response.json({
|
||||
success: true,
|
||||
data: {
|
||||
berita: latestBerita
|
||||
? { id: latestBerita.id, createdAt: latestBerita.createdAt.toISOString() }
|
||||
: null,
|
||||
pengumuman: latestPengumuman
|
||||
? { id: latestPengumuman.id, createdAt: latestPengumuman.createdAt.toISOString() }
|
||||
: null,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error in /api/check-update:", error);
|
||||
return Response.json(
|
||||
{ success: false, message: "Gagal cek update" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
43
src/app/api/news/latest/route.ts
Normal file
43
src/app/api/news/latest/route.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// app/api/news/latest/route.ts
|
||||
import { NextResponse } from "next/server";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const berita = await prisma.berita.findMany({
|
||||
take: 3,
|
||||
orderBy: { createdAt: "desc" },
|
||||
include: { kategoriBerita: true },
|
||||
});
|
||||
|
||||
const pengumuman = await prisma.pengumuman.findMany({
|
||||
take: 3,
|
||||
orderBy: { createdAt: "desc" },
|
||||
include: { CategoryPengumuman: true },
|
||||
});
|
||||
|
||||
const news = [
|
||||
...berita.map((b) => ({
|
||||
id: b.id,
|
||||
type: "berita" as const,
|
||||
title: b.judul,
|
||||
content: b.content,
|
||||
timestamp: b.createdAt,
|
||||
kategoriBerita: b.kategoriBerita || undefined,
|
||||
})),
|
||||
...pengumuman.map((p) => ({
|
||||
id: p.id,
|
||||
type: "pengumuman" as const,
|
||||
title: p.judul,
|
||||
content: p.content,
|
||||
timestamp: p.createdAt,
|
||||
kategoriPengumuman: p.CategoryPengumuman || undefined,
|
||||
})),
|
||||
];
|
||||
|
||||
return NextResponse.json({ success: true, news }); // ✅ ganti 'data' jadi 'news'
|
||||
} catch (error) {
|
||||
console.error("API Error:", error);
|
||||
return NextResponse.json({ success: false, error: "Gagal memuat data" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -1,74 +1,94 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { Box, Paper, Text, Group, CloseButton, Badge, ActionIcon, Stack, Transition } from "@mantine/core";
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
Box,
|
||||
CloseButton,
|
||||
Group,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
Transition,
|
||||
} from "@mantine/core";
|
||||
import { IconBell, IconChevronRight } from "@tabler/icons-react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface NewsItem {
|
||||
id: string | number;
|
||||
// === Tipe yang bisa diimpor di tempat lain ===
|
||||
export interface KategoriBerita {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface KategoriPengumuman {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface NewsItem {
|
||||
id: string;
|
||||
type: "berita" | "pengumuman";
|
||||
title: string;
|
||||
content: string;
|
||||
timestamp?: string | Date;
|
||||
kategoriBerita?: KategoriBerita;
|
||||
kategoriPengumuman?: KategoriPengumuman;
|
||||
}
|
||||
|
||||
interface ModernNewsNotificationProps {
|
||||
export interface ModernNewsNotificationProps {
|
||||
news: NewsItem[];
|
||||
hasNewContent?: boolean; // ✅ TAMBAHAN
|
||||
newItemCount?: number; // ← tambahkan ini
|
||||
onSeen?: () => void; // ✅ TAMBAHAN
|
||||
hasNewContent?: boolean;
|
||||
newItemCount?: number;
|
||||
onSeen?: () => void;
|
||||
autoShowDelay?: number;
|
||||
}
|
||||
|
||||
// === Helper ===
|
||||
function stripHtml(html: string): string {
|
||||
return html
|
||||
.replace(/<[^>]+>/g, '')
|
||||
.replace(/ /gi, ' ')
|
||||
.replace(/&/gi, '&')
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/<[^>]+>/g, "")
|
||||
.replace(/ /gi, " ")
|
||||
.replace(/&/gi, "&")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
// === Komponen Utama ===
|
||||
export default function ModernNewsNotification({
|
||||
news = [],
|
||||
hasNewContent = false,
|
||||
newItemCount = 0, // 👈 tambahkan ini
|
||||
newItemCount = 0,
|
||||
onSeen,
|
||||
autoShowDelay = 2000,
|
||||
}: ModernNewsNotificationProps) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
const [toastVisible, setToastVisible] = useState(false);
|
||||
const [widgetOpen, setWidgetOpen] = useState(false);
|
||||
const [hasNewNotifications, setHasNewNotifications] = useState(hasNewContent);
|
||||
const [hasShownToast, setHasShownToast] = useState(false);
|
||||
const [iconVisible, setIconVisible] = useState(true);
|
||||
const pathname = usePathname();
|
||||
|
||||
// Sinkronisasi dari luar
|
||||
// Sinkronisasi prop eksternal
|
||||
useEffect(() => {
|
||||
if (hasNewContent) {
|
||||
setHasNewNotifications(true);
|
||||
// Jangan otomatis tampilkan toast di sini — biarkan saat page load saja
|
||||
}
|
||||
setHasNewNotifications(hasNewContent);
|
||||
}, [hasNewContent]);
|
||||
|
||||
// Auto show toast hanya saat page pertama kali load
|
||||
// Tampilkan toast pertama kali
|
||||
useEffect(() => {
|
||||
if (news.length > 0 && !toastVisible && !hasShownToast) {
|
||||
const timer = setTimeout(() => {
|
||||
setToastVisible(true);
|
||||
setHasShownToast(true);
|
||||
// Jika ada new content, anggap sudah "dilihat" setelah toast muncul
|
||||
if (hasNewNotifications) {
|
||||
onSeen?.();
|
||||
}
|
||||
if (hasNewNotifications) onSeen?.();
|
||||
}, autoShowDelay);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [news.length, autoShowDelay, toastVisible, hasShownToast, hasNewNotifications, onSeen]);
|
||||
|
||||
// Auto hide toast
|
||||
// Sembunyikan toast otomatis
|
||||
useEffect(() => {
|
||||
if (toastVisible) {
|
||||
const timer = setTimeout(() => setToastVisible(false), 8000);
|
||||
@@ -76,7 +96,7 @@ export default function ModernNewsNotification({
|
||||
}
|
||||
}, [toastVisible]);
|
||||
|
||||
// Scroll handler
|
||||
// Kontrol visibilitas ikon saat scroll
|
||||
useEffect(() => {
|
||||
let lastScrollY = window.scrollY;
|
||||
const HIDE_THRESHOLD = 100;
|
||||
@@ -84,11 +104,11 @@ export default function ModernNewsNotification({
|
||||
|
||||
const handleScroll = () => {
|
||||
const currentScrollY = window.scrollY;
|
||||
const scrollDirection = currentScrollY > lastScrollY ? 'down' : 'up';
|
||||
const scrollDirection = currentScrollY > lastScrollY ? "down" : "up";
|
||||
|
||||
if (scrollDirection === 'down' && currentScrollY > HIDE_THRESHOLD) {
|
||||
if (scrollDirection === "down" && currentScrollY > HIDE_THRESHOLD) {
|
||||
setIconVisible(false);
|
||||
} else if (scrollDirection === 'up' && currentScrollY < SHOW_THRESHOLD) {
|
||||
} else if (scrollDirection === "up" && currentScrollY < SHOW_THRESHOLD) {
|
||||
setIconVisible(true);
|
||||
}
|
||||
|
||||
@@ -99,19 +119,25 @@ export default function ModernNewsNotification({
|
||||
lastScrollY = currentScrollY;
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
window.addEventListener("scroll", handleScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, [toastVisible]);
|
||||
|
||||
const currentNews = news[0];
|
||||
|
||||
// 🔗 Arahkan ke detail dengan kategori aman
|
||||
const handleNotificationClick = (item: NewsItem) => {
|
||||
setWidgetOpen(false);
|
||||
onSeen?.(); // ✅ tandai sebagai dilihat
|
||||
onSeen?.();
|
||||
|
||||
if (item.type === "berita") {
|
||||
router.push("/darmasaba/desa/berita/semua");
|
||||
const kategori = item.kategoriBerita?.name || "umum";
|
||||
const safeKategori = encodeURIComponent(kategori);
|
||||
router.push(`/darmasaba/desa/berita/${safeKategori}/${item.id}`);
|
||||
} else if (item.type === "pengumuman") {
|
||||
router.push("/darmasaba/desa/pengumuman");
|
||||
const kategori = item.kategoriPengumuman?.name || "umum";
|
||||
const safeKategori = encodeURIComponent(kategori);
|
||||
router.push(`/darmasaba/desa/pengumuman/${safeKategori}/${item.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -119,35 +145,40 @@ export default function ModernNewsNotification({
|
||||
setToastVisible(false);
|
||||
setWidgetOpen(true);
|
||||
setHasNewNotifications(false);
|
||||
onSeen?.(); // ✅
|
||||
onSeen?.();
|
||||
};
|
||||
|
||||
const handleDismissToast = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
setToastVisible(false);
|
||||
onSeen?.(); // ✅
|
||||
onSeen?.();
|
||||
};
|
||||
|
||||
// Only show on landing page
|
||||
if (pathname !== '/darmasaba') {
|
||||
return null;
|
||||
}
|
||||
// Hanya tampilkan di landing page
|
||||
if (pathname !== "/darmasaba") return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Floating Bell Icon */}
|
||||
<Transition mounted={iconVisible} transition="slide-down" duration={200}>
|
||||
{(transitionStyles) => (
|
||||
<Box style={{ ...transitionStyles, position: "fixed", bottom: "24px", right: "24px" }}>
|
||||
<Box
|
||||
style={{
|
||||
...transitionStyles,
|
||||
position: "fixed",
|
||||
bottom: "24px",
|
||||
right: "24px",
|
||||
}}
|
||||
>
|
||||
<ActionIcon
|
||||
size="xl"
|
||||
radius="xl"
|
||||
variant="filled"
|
||||
color="#1e5a7e"
|
||||
onClick={() => {
|
||||
setWidgetOpen(!widgetOpen);
|
||||
setWidgetOpen((open) => !open);
|
||||
setHasNewNotifications(false);
|
||||
onSeen?.(); // ✅
|
||||
onSeen?.();
|
||||
}}
|
||||
style={{
|
||||
width: "60px",
|
||||
@@ -168,7 +199,6 @@ export default function ModernNewsNotification({
|
||||
right: "6px",
|
||||
minWidth: "22px",
|
||||
height: "22px",
|
||||
padding: "0 6px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
@@ -208,12 +238,14 @@ export default function ModernNewsNotification({
|
||||
<Group justify="space-between">
|
||||
<Group gap="xs">
|
||||
<IconBell size={20} />
|
||||
<Text c="white" fw={600} size="md">Berita & Pengumuman</Text>
|
||||
<Text c="white" fw={600} size="md">
|
||||
Berita & Pengumuman
|
||||
</Text>
|
||||
</Group>
|
||||
<CloseButton
|
||||
onClick={() => {
|
||||
setWidgetOpen(false);
|
||||
onSeen?.(); // ✅
|
||||
onSeen?.();
|
||||
}}
|
||||
variant="transparent"
|
||||
c="white"
|
||||
@@ -224,13 +256,15 @@ export default function ModernNewsNotification({
|
||||
<Box style={{ maxHeight: "400px", overflowY: "auto", padding: "12px" }}>
|
||||
{news.length === 0 ? (
|
||||
<Box p="xl" style={{ textAlign: "center" }}>
|
||||
<Text c="dimmed" size="sm">Tidak ada berita terbaru</Text>
|
||||
<Text c="dimmed" size="sm">
|
||||
Tidak ada berita terbaru
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<Stack gap="xs">
|
||||
{news.map((item, index) => (
|
||||
{news.map((item) => (
|
||||
<Paper
|
||||
key={item.id || index}
|
||||
key={item.id}
|
||||
p="md"
|
||||
radius="md"
|
||||
style={{
|
||||
@@ -276,7 +310,11 @@ export default function ModernNewsNotification({
|
||||
</Transition>
|
||||
|
||||
{/* Toast Notification */}
|
||||
<Transition mounted={toastVisible && !!currentNews} transition="slide-left" duration={300}>
|
||||
<Transition
|
||||
mounted={toastVisible && !!currentNews}
|
||||
transition="slide-left"
|
||||
duration={300}
|
||||
>
|
||||
{(styles) => (
|
||||
<Paper
|
||||
style={{
|
||||
@@ -314,7 +352,9 @@ export default function ModernNewsNotification({
|
||||
variant="light"
|
||||
leftSection={currentNews?.type === "berita" ? "📰" : "📢"}
|
||||
>
|
||||
{currentNews?.type === "berita" ? "Berita Terbaru" : "Pengumuman"}
|
||||
{currentNews?.type === "berita"
|
||||
? "Berita Terbaru"
|
||||
: "Pengumuman"}
|
||||
</Badge>
|
||||
<CloseButton onClick={handleDismissToast} size="sm" />
|
||||
</Group>
|
||||
@@ -329,7 +369,7 @@ export default function ModernNewsNotification({
|
||||
|
||||
<Group justify="space-between" mt="md">
|
||||
<Text size="xs" c="dimmed">
|
||||
{news.length > 1 ? `${news.length} berita tersedia` : '1 berita'}
|
||||
{news.length > 1 ? `${news.length} berita tersedia` : "1 berita"}
|
||||
</Text>
|
||||
<Text
|
||||
size="xs"
|
||||
|
||||
@@ -60,7 +60,7 @@ function Penghargaan() {
|
||||
<Box
|
||||
onClick={() => setShowVideo(true)}
|
||||
style={{
|
||||
backgroundImage: "url('/assets/images/award-poster.jpg')",
|
||||
backgroundImage: "url('/mangupuraaward.jpeg')",
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
cursor: 'pointer',
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user