Add Oudelaa dashboard API integration
فشلت بعض الفحوصات
Deploy To Ghaymah / deploy (push) Has been cancelled
فشلت بعض الفحوصات
Deploy To Ghaymah / deploy (push) Has been cancelled
هذا الالتزام موجود في:
161
oudelaa_dashboard/app/(dashboard)/messages/page.tsx
Normal file
161
oudelaa_dashboard/app/(dashboard)/messages/page.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { NoPermissionState } from "@/components/auth/no-permission-state";
|
||||
import { useSuperAdminSession } from "@/components/auth/session-context";
|
||||
import { PageHeader } from "@/components/dashboard/page-header";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { EmptyState } from "@/components/ui/empty-state";
|
||||
import { useToast } from "@/components/ui/toast";
|
||||
import { listModerationComments } from "@/lib/api/comments";
|
||||
import { getItems } from "@/lib/api/core";
|
||||
import { listPlatformNotifications } from "@/lib/api/notifications";
|
||||
import { formatDateTime } from "@/lib/format";
|
||||
import { getCommentAuthor, getUserLabel } from "@/lib/post-utils";
|
||||
import { SUPERADMIN_PERMISSIONS, hasPermission } from "@/lib/permissions";
|
||||
import type { ApiComment, NotificationItem, NotificationsResponse } from "@/types/api";
|
||||
|
||||
const EMPTY_NOTIFICATIONS: NotificationsResponse = { items: [], data: [], unreadCount: 0 };
|
||||
|
||||
export default function MessagesPage() {
|
||||
const { permissions } = useSuperAdminSession();
|
||||
const [comments, setComments] = useState<ApiComment[]>([]);
|
||||
const [alerts, setAlerts] = useState<NotificationItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { toast } = useToast();
|
||||
|
||||
const canReadNotifications = hasPermission(
|
||||
permissions,
|
||||
SUPERADMIN_PERMISSIONS.NOTIFICATIONS_READ,
|
||||
);
|
||||
const canModerateContent = hasPermission(
|
||||
permissions,
|
||||
SUPERADMIN_PERMISSIONS.CONTENT_MODERATE,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
|
||||
const loadData = async () => {
|
||||
if (!canReadNotifications && !canModerateContent) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const [commentsResponse, notificationsResponse] = await Promise.all([
|
||||
canModerateContent
|
||||
? listModerationComments({ page: 1, limit: 8, sortOrder: "desc" })
|
||||
: Promise.resolve({ items: [], data: [] }),
|
||||
canReadNotifications
|
||||
? listPlatformNotifications({ page: 1, limit: 8, sortOrder: "desc" })
|
||||
: Promise.resolve(EMPTY_NOTIFICATIONS),
|
||||
]);
|
||||
|
||||
if (!active) return;
|
||||
|
||||
setComments(getItems(commentsResponse) as ApiComment[]);
|
||||
setAlerts(
|
||||
(getItems(notificationsResponse) as NotificationItem[]).filter((item) =>
|
||||
["message", "mention", "comment"].includes(item.type),
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
if (!active) return;
|
||||
toast({ title: "Failed to load engagement follow-up", description: String(error), variant: "danger" });
|
||||
} finally {
|
||||
if (active) setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
void loadData();
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [canModerateContent, canReadNotifications, toast]);
|
||||
|
||||
if (!canReadNotifications && !canModerateContent) {
|
||||
return (
|
||||
<div className="space-y-5 pb-8">
|
||||
<PageHeader
|
||||
title="Engagement follow-up"
|
||||
subtitle="A consolidated view of message-related alerts, mentions, and recent comments."
|
||||
/>
|
||||
<NoPermissionState description="This page needs notifications.read, content.moderate, or both." />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-5 pb-8">
|
||||
<PageHeader
|
||||
title="Engagement follow-up"
|
||||
subtitle="A consolidated view of message-related alerts, mentions, and recent comments."
|
||||
/>
|
||||
|
||||
<section className="grid gap-4 xl:grid-cols-12">
|
||||
{canReadNotifications ? (
|
||||
<Card className={canModerateContent ? "xl:col-span-5" : "xl:col-span-12"}>
|
||||
<CardHeader>
|
||||
<CardTitle>Interaction alerts</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{!alerts.length && !loading ? (
|
||||
<EmptyState title="No alerts" description="No message, mention, or comment alerts are available right now." />
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{alerts.map((item) => (
|
||||
<div key={item._id} className="rounded-xl border border-border/70 bg-secondary/20 p-4">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<p className="text-sm font-medium text-foreground">{item.title ?? item.type}</p>
|
||||
<Badge variant={item.read ? "muted" : "warning"}>{item.type}</Badge>
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-muted-foreground">{item.previewText ?? "-"}</p>
|
||||
<p className="mt-2 text-xs text-muted-foreground">{formatDateTime(item.createdAt)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
{canModerateContent ? (
|
||||
<Card className={canReadNotifications ? "xl:col-span-7" : "xl:col-span-12"}>
|
||||
<CardHeader>
|
||||
<CardTitle>Recent public comments</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{!comments.length && !loading ? (
|
||||
<EmptyState title="No comments" description="No comments are available for follow-up right now." />
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{comments.map((comment) => (
|
||||
<div key={comment._id} className="rounded-xl border border-border/70 bg-secondary/20 p-4">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-foreground">
|
||||
{getUserLabel(getCommentAuthor(comment))}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{formatDateTime(comment.createdAt)}</p>
|
||||
</div>
|
||||
<Badge variant={comment.mentionUsernames?.length ? "warning" : "muted"}>
|
||||
{comment.mentionUsernames?.length ? "mention" : "comment"}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="mt-3 text-sm text-foreground">{comment.content}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : null}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
المرجع في مشكلة جديدة
حظر مستخدم