diff --git a/frontends/nextjs/src/components/level/panels/ModeratorPanel.tsx b/frontends/nextjs/src/components/level/panels/ModeratorPanel.tsx index 514ccdb25..f811601a0 100644 --- a/frontends/nextjs/src/components/level/panels/ModeratorPanel.tsx +++ b/frontends/nextjs/src/components/level/panels/ModeratorPanel.tsx @@ -1,22 +1,13 @@ "use client" import { useEffect, useMemo, useState } from 'react' -import { Button } from '@/components/ui' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui' -import { Badge } from '@/components/ui' -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui' -import { Stack, Typography } from '@/components/ui' import { toast } from 'sonner' +import { AppHeader } from '@/components/shared/AppHeader' import { Database } from '@/lib/database' import type { Comment, User } from '@/lib/level-types' -import { AppHeader } from '@/components/shared/AppHeader' +import { ModeratorActions } from './ModeratorPanel/Actions' +import { ModeratorHeader } from './ModeratorPanel/Header' +import { ModeratorLogList } from './ModeratorPanel/LogList' const FLAGGED_TERMS = ['spam', 'error', 'abuse', 'illegal', 'urgent', 'offensive'] @@ -70,8 +61,6 @@ export function ModeratorPanel({ user, onLogout, onNavigate }: ModeratorPanelPro toast.success('Flag resolved and archived from the queue') } - const highlightLabel = (term: string) => term.charAt(0).toUpperCase() + term.slice(1) - return (
-
- Moderation queue - - Keep the community healthy by resolving flags, reviewing reports, and guiding the tone. - -
- -
- - - Flagged content - Automated signal based on keywords - - - {flaggedComments.length} - - Pending items in the moderation queue - - - - - - - Resolved this session - - - {resolvedIds.length} - - Items you flagged as handled - - - - - - - Community signals - - - - {FLAGGED_TERMS.map((term) => ( - {highlightLabel(term)} - ))} - - - Track the keywords that pulled items into the queue - - - -
- - - -
-
- Flagged comments - A curated view of the comments that triggered a signal -
- -
-
- - {isLoading ? ( - Loading flagged comments… - ) : flaggedComments.length === 0 ? ( - - No flagged comments at the moment. Enjoy the calm. - - ) : ( - - - - User - Comment - Matched terms - Actions - - - - {flaggedComments.map((comment) => { - const matches = FLAGGED_TERMS.filter((term) => - comment.content.toLowerCase().includes(term) - ) - return ( - - {comment.userId} - {comment.content} - - - {matches.map((match) => ( - - {match} - - ))} - - - - - - - ) - })} - -
- )} -
-
+ + +
) diff --git a/frontends/nextjs/src/components/level/panels/ModeratorPanel/Actions.tsx b/frontends/nextjs/src/components/level/panels/ModeratorPanel/Actions.tsx new file mode 100644 index 000000000..8bfa78c49 --- /dev/null +++ b/frontends/nextjs/src/components/level/panels/ModeratorPanel/Actions.tsx @@ -0,0 +1,56 @@ +import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle, Stack, Typography } from '@/components/ui' + +interface ModeratorActionsProps { + flaggedCount: number + resolvedCount: number + flaggedTerms: string[] +} + +export function ModeratorActions({ flaggedCount, resolvedCount, flaggedTerms }: ModeratorActionsProps) { + const highlightLabel = (term: string) => term.charAt(0).toUpperCase() + term.slice(1) + + return ( +
+ + + Flagged content + Automated signal based on keywords + + + {flaggedCount} + + Pending items in the moderation queue + + + + + + + Resolved this session + + + {resolvedCount} + + Items you flagged as handled + + + + + + + Community signals + + + + {flaggedTerms.map((term) => ( + {highlightLabel(term)} + ))} + + + Track the keywords that pulled items into the queue + + + +
+ ) +} diff --git a/frontends/nextjs/src/components/level/panels/ModeratorPanel/Header.tsx b/frontends/nextjs/src/components/level/panels/ModeratorPanel/Header.tsx new file mode 100644 index 000000000..cb86c4a0a --- /dev/null +++ b/frontends/nextjs/src/components/level/panels/ModeratorPanel/Header.tsx @@ -0,0 +1,12 @@ +import { Typography } from '@/components/ui' + +export function ModeratorHeader() { + return ( +
+ Moderation queue + + Keep the community healthy by resolving flags, reviewing reports, and guiding the tone. + +
+ ) +} diff --git a/frontends/nextjs/src/components/level/panels/ModeratorPanel/LogList.tsx b/frontends/nextjs/src/components/level/panels/ModeratorPanel/LogList.tsx new file mode 100644 index 000000000..b133c6b0b --- /dev/null +++ b/frontends/nextjs/src/components/level/panels/ModeratorPanel/LogList.tsx @@ -0,0 +1,83 @@ +import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Stack } from '@/components/ui' +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from '@/components/ui' +import type { Comment } from '@/lib/level-types' + +interface ModeratorLogListProps { + flaggedComments: Comment[] + flaggedTerms: string[] + isLoading: boolean + onNavigate: (level: number) => void + onResolve: (commentId: string) => void +} + +export function ModeratorLogList({ + flaggedComments, + flaggedTerms, + isLoading, + onNavigate, + onResolve, +}: ModeratorLogListProps) { + return ( + + +
+
+ Flagged comments + A curated view of the comments that triggered a signal +
+ +
+
+ + {isLoading ? ( + Loading flagged comments… + ) : flaggedComments.length === 0 ? ( + + No flagged comments at the moment. Enjoy the calm. + + ) : ( + + + + User + Comment + Matched terms + Actions + + + + {flaggedComments.map((comment) => { + const matches = flaggedTerms.filter((term) => + comment.content.toLowerCase().includes(term) + ) + + return ( + + {comment.userId} + {comment.content} + + + {matches.map((match) => ( + + {match} + + ))} + + + + + + + ) + })} + +
+ )} +
+
+ ) +}