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}
+
+ ))}
+
+
+
+
+
+
+ )
+ })}
+
+
+ )}
+
+
+ )
+}