TorbatYar/frontend/app/notifications/page.tsx
2026-07-28 20:39:10 +03:30

130 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
import { AuthGuard } from "@/components/AuthGuard";
import { Button } from "@/components/ui";
import { loadCommercialNotifications } from "@/modules/commercial/adapters";
import type { AdapterResult, CommercialNotification } from "@/modules/commercial/types";
import {
CommercialEmptyState,
CommercialSkeleton,
PlatformShell,
} from "@/modules/commercial/components";
type Filter = "all" | "unread" | "high" | "medium" | "low";
function NotificationsInner() {
const [result, setResult] = useState<AdapterResult<CommercialNotification[]> | null>(null);
const [filter, setFilter] = useState<Filter>("all");
const [loading, setLoading] = useState(true);
useEffect(() => {
void loadCommercialNotifications().then((r) => {
setResult(r);
setLoading(false);
});
}, []);
const items = useMemo(() => {
const list = result?.data || [];
return list.filter((n) => {
if (filter === "unread") return !n.read;
if (filter === "high" || filter === "medium" || filter === "low") {
const p = (n.priority || n.severity || "").toLowerCase();
return p === filter || (filter === "high" && (p === "critical" || p === "urgent"));
}
return true;
});
}, [result, filter]);
return (
<PlatformShell title="اعلان‌ها" subtitle="فقط از commercial notifications API — بدون داده جعلی">
<div className="mb-4 flex flex-wrap gap-2" role="tablist" aria-label="فیلتر اعلان">
{(
[
["all", "همه"],
["unread", "خوانده‌نشده"],
["high", "اولویت بالا"],
["medium", "متوسط"],
["low", "پایین"],
] as const
).map(([id, label]) => (
<button
key={id}
type="button"
role="tab"
aria-selected={filter === id}
className={`rounded-full px-3 py-1.5 text-xs font-medium ${
filter === id
? "bg-primary text-white"
: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-300"
}`}
onClick={() => setFilter(id)}
>
{label}
</button>
))}
</div>
{loading ? <CommercialSkeleton rows={4} /> : null}
{!loading && (!result || result.availability !== "ready" || !items.length) ? (
<CommercialEmptyState
title="اعلانی نیست"
description={result?.message || "وقتی اعلان از بک‌اند برسد اینجا نمایش داده می‌شود."}
actionHref="/dashboard"
actionLabel="داشبورد"
secondaryHref="/help"
secondaryLabel="راهنما"
/>
) : null}
{!loading && items.length ? (
<ul className="space-y-2">
{items.map((n) => (
<li
key={n.id}
className={`rounded-xl border px-4 py-3 text-sm ${
n.read
? "border-gray-100 bg-white dark:border-gray-800 dark:bg-gray-900"
: "border-primary/30 bg-[var(--color-primary-soft)]"
}`}
>
<div className="flex flex-wrap items-center justify-between gap-2">
<p className="font-semibold text-secondary dark:text-gray-100">{n.title}</p>
<span className="text-[10px] uppercase text-gray-400">
{n.priority || n.severity || "normal"}
{!n.read ? " · unread" : ""}
</span>
</div>
{n.body ? <p className="mt-1 text-xs text-gray-600 dark:text-gray-400">{n.body}</p> : null}
{n.href ? (
<Link href={n.href} className="mt-2 inline-block text-xs font-semibold text-primary">
باز کردن
</Link>
) : null}
</li>
))}
</ul>
) : null}
<div className="mt-6">
<Link href="/dashboard">
<Button variant="outline" size="sm">
بازگشت
</Button>
</Link>
</div>
</PlatformShell>
);
}
export default function NotificationsPage() {
return (
<AuthGuard>
<NotificationsInner />
</AuthGuard>
);
}