From 1973b8b904e75bb86ff0098cb24ea4897d560f17 Mon Sep 17 00:00:00 2001 From: boutmoun123 Date: Sun, 31 May 2026 17:05:58 +0300 Subject: [PATCH] fix: split collaboration request controllers --- .../app/(dashboard)/content/page.tsx | 81 ++++++++- .../app/(dashboard)/messages/page.tsx | 30 ++++ .../app/(dashboard)/notifications/page.tsx | 37 +++- .../app/(dashboard)/reports/page.tsx | 17 ++ .../app/(dashboard)/security/page.tsx | 36 ++++ .../components/dashboard/media-inspector.tsx | 162 ++++++++++++++++++ oudelaa_dashboard/types/api.ts | 11 ++ .../collaboration-requests.controller.ts | 19 +- .../collaboration-requests.module.ts | 3 +- ...posts-collaboration-requests.controller.ts | 24 +++ 10 files changed, 402 insertions(+), 18 deletions(-) create mode 100644 oudelaa_dashboard/components/dashboard/media-inspector.tsx create mode 100644 src/modules/collaboration-requests/posts-collaboration-requests.controller.ts diff --git a/oudelaa_dashboard/app/(dashboard)/content/page.tsx b/oudelaa_dashboard/app/(dashboard)/content/page.tsx index 52a7b5f..e73ae7b 100644 --- a/oudelaa_dashboard/app/(dashboard)/content/page.tsx +++ b/oudelaa_dashboard/app/(dashboard)/content/page.tsx @@ -1,14 +1,16 @@ "use client"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, 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 { MediaInspector, getMediaHealth } from "@/components/dashboard/media-inspector"; import { PaginationControls } from "@/components/dashboard/pagination-controls"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Drawer } from "@/components/ui/drawer"; import { EmptyState } from "@/components/ui/empty-state"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; @@ -89,6 +91,7 @@ export default function ContentPage() { const [bulkAction, setBulkAction] = useState("flag"); const [bulkReason, setBulkReason] = useState(""); const [bulkLoading, setBulkLoading] = useState(false); + const [selectedPost, setSelectedPost] = useState(null); const { toast } = useToast(); const filtersRef = useRef({ query, postType, visibility, moderationStatus }); @@ -165,6 +168,13 @@ export default function ContentPage() { const posts = getItems(postsResponse) as ApiPost[]; const comments = getItems(commentsResponse) as ApiComment[]; const bulkIds = bulkTarget === "post" ? selectedPostIds : selectedCommentIds; + const mediaSummary = useMemo(() => { + const mediaPosts = posts.filter((post) => ["image", "video", "audio"].includes(post.postType ?? "")); + const unhealthy = mediaPosts.filter((post) => getMediaHealth(post).problems.length > 0); + const hlsReady = posts.filter((post) => post.postType === "video" && post.hlsUrl).length; + const thumbnailsReady = posts.filter((post) => post.thumbnailUrl || post.thumbnailVariants?.thumbnail).length; + return { mediaPosts: mediaPosts.length, unhealthy: unhealthy.length, hlsReady, thumbnailsReady }; + }, [posts]); const updatePostStatus = async (postId: string, status: ModerationStatus) => { const reason = promptReason(`post ${status}`); @@ -328,6 +338,33 @@ export default function ContentPage() { +
+ + + Media posts + + {mediaSummary.mediaPosts} + + + + Needs media review + + {mediaSummary.unhealthy} + + + + HLS ready videos + + {mediaSummary.hlsReady} + + + + Thumbnails ready + + {mediaSummary.thumbnailsReady} + +
+ {canUseBulkActions ? ( @@ -420,7 +457,14 @@ export default function ContentPage() { ) : null} {post.content || "-"} {getUserLabel(getPostAuthor(post))} - {post.postType ?? "-"} + +
+
{post.postType ?? "-"}
+ + {getMediaHealth(post).problems.length ? "media check" : "ok"} + +
+
+ @@ -546,6 +593,36 @@ export default function ContentPage() {
+ + setSelectedPost(null)} + title="Post media details" + description="Preview the actual media, inspect generated URLs, and review moderation state." + side="right" + widthClassName="w-full sm:w-[92vw] sm:max-w-3xl" + > + {selectedPost ? ( +
+ + + + Post metadata + + +
Author: {getUserLabel(getPostAuthor(selectedPost))}
+
Type: {selectedPost.postType ?? "-"}
+
Status: {selectedPost.moderationStatus ?? "active"}
+
Processing: {selectedPost.processingStatus ?? "-"}
+
Visibility: {selectedPost.visibility ?? "-"}
+
Created: {formatDateTime(selectedPost.createdAt)}
+
Likes: {selectedPost.likesCount ?? 0}
+
Comments: {selectedPost.commentsCount ?? 0}
+
+
+
+ ) : null} +
); } diff --git a/oudelaa_dashboard/app/(dashboard)/messages/page.tsx b/oudelaa_dashboard/app/(dashboard)/messages/page.tsx index 431eb68..dc3d336 100644 --- a/oudelaa_dashboard/app/(dashboard)/messages/page.tsx +++ b/oudelaa_dashboard/app/(dashboard)/messages/page.tsx @@ -97,6 +97,36 @@ export default function MessagesPage() { subtitle="A consolidated view of message-related alerts, mentions, and recent comments." /> +
+ + + Socket namespace + + + Socket.IO +
/chat
+
+
+ + + Message event + + + new_message +
REST and socket sends broadcast to conversation rooms.
+
+
+ + + Room pattern + + + conversation:ID +
Flutter should join when opening a chat and leave when closing it.
+
+
+
+
{canReadNotifications ? ( diff --git a/oudelaa_dashboard/app/(dashboard)/notifications/page.tsx b/oudelaa_dashboard/app/(dashboard)/notifications/page.tsx index 6faf789..4a3676e 100644 --- a/oudelaa_dashboard/app/(dashboard)/notifications/page.tsx +++ b/oudelaa_dashboard/app/(dashboard)/notifications/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { NoPermissionState } from "@/components/auth/no-permission-state"; import { useSuperAdminSession } from "@/components/auth/session-context"; @@ -82,6 +82,16 @@ export default function NotificationsPage() { }; const items = getItems(response) as NotificationItem[]; + const health = useMemo(() => { + const missingDeepLink = items.filter((item) => item.resourceType !== "system" && !item.deepLink).length; + const messageLinks = items.filter( + (item) => item.type === "message" && item.deepLink?.startsWith("/chat/conversations/"), + ).length; + const postLinks = items.filter( + (item) => ["like", "comment", "reply", "mention"].includes(item.type) && item.deepLink?.startsWith("/posts/"), + ).length; + return { missingDeepLink, messageLinks, postLinks }; + }, [items]); if (!canReadNotifications) { return ( @@ -130,6 +140,9 @@ export default function NotificationsPage() { save share mention + reply + system + collaboration_request