feat(moderation-specific): Add threat level moderation page with API integration, UI components, and navigation updates

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Lilith 2026-03-13 07:02:38 -07:00
parent 733b804bd5
commit 46e73d76a8
6 changed files with 1375 additions and 0 deletions

View file

@ -98,6 +98,7 @@ import {
ModerationOverviewPage,
ModerationQueuePage,
ModerationHistoryPage,
ModerationThreatLevelsPage,
} from './pages/moderation';
// Authentication
@ -243,6 +244,7 @@ const AuthenticatedRoutes = () => (
<Route path="/moderation" element={<ModerationOverviewPage />} />
<Route path="/moderation/queue" element={<ModerationQueuePage />} />
<Route path="/moderation/history" element={<ModerationHistoryPage />} />
<Route path="/moderation/threat-levels" element={<ModerationThreatLevelsPage />} />
{/* CATCH-ALL 404 */}
<Route path="*" element={

View file

@ -248,6 +248,11 @@ export const NAVIGATION_SECTIONS: NavSection[] = [
label: 'History',
description: 'Full audit trail of all scored content',
},
{
to: '/moderation/threat-levels',
label: 'Threat Levels',
description: 'User threat escalation levels and admin overrides',
},
],
},

View file

@ -9,6 +9,10 @@ import type {
ModerationQueueItem,
ModerationStats,
ModerationHistoryFilters,
PaginatedThreatLevels,
ThreatLevelFilters,
UserThreatLevel,
ThreatEscalationEvent,
} from './types'
const MODERATION_API_URL = import.meta.env.VITE_MODERATION_API_URL || '/api/content-moderation'
@ -88,3 +92,58 @@ export async function fetchModerationHistory(
if (filters.endDate) { params.set('endDate', filters.endDate) }
return moderationRequest<PaginatedModerationQueue>(`/queue/history?${params}`)
}
/**
* Fetch paginated list of user threat levels with optional filters
*/
export async function fetchThreatLevels(
filters: ThreatLevelFilters,
): Promise<PaginatedThreatLevels> {
const params = new URLSearchParams()
params.set('page', String(filters.page))
params.set('limit', String(filters.limit))
if (filters.level) { params.set('level', filters.level) }
if (filters.minScore !== undefined) { params.set('minScore', String(filters.minScore)) }
if (filters.maxScore !== undefined) { params.set('maxScore', String(filters.maxScore)) }
return moderationRequest<PaginatedThreatLevels>(`/threat-levels?${params}`)
}
/**
* Fetch a single user's threat level record
*/
export async function fetchThreatLevel(userId: string): Promise<UserThreatLevel> {
return moderationRequest<UserThreatLevel>(`/threat-levels/${userId}`)
}
/**
* Fetch escalation history timeline for a user
*/
export async function fetchEscalationHistory(userId: string): Promise<ThreatEscalationEvent[]> {
return moderationRequest<ThreatEscalationEvent[]>(`/threat-levels/${userId}/history`)
}
/**
* Apply an admin override to a user's threat level
*/
export async function applyThreatOverride(
userId: string,
body: { level: string; adminId: string; notes: string },
): Promise<UserThreatLevel> {
return moderationRequest<UserThreatLevel>(`/threat-levels/${userId}`, {
method: 'PATCH',
body: JSON.stringify(body),
})
}
/**
* Reset a user's threat level to SAFE and clear all violation counters
*/
export async function resetThreatLevel(
userId: string,
body: { adminId: string; notes: string },
): Promise<UserThreatLevel> {
return moderationRequest<UserThreatLevel>(`/threat-levels/${userId}/reset`, {
method: 'POST',
body: JSON.stringify(body),
})
}

View file

@ -1,3 +1,4 @@
export { OverviewPage as ModerationOverviewPage } from './overview-page'
export { QueuePage as ModerationQueuePage } from './queue-page'
export { HistoryPage as ModerationHistoryPage } from './history-page'
export { ThreatLevelsPage as ModerationThreatLevelsPage } from './threat-levels-page'

View file

@ -87,3 +87,69 @@ export interface ModerationHistoryFilters {
startDate?: string
endDate?: string
}
// ─── Threat Escalation Types ──────────────────────────────────────────────────
export type ThreatLevel = 'safe' | 'caution' | 'warning' | 'danger' | 'suspended'
export type EscalationTrigger =
| 'moderation_violation'
| 'admin_override'
| 'decay'
| 'admin_reset'
export interface UserThreatLevel {
id: string
userId: string
score: number
level: ThreatLevel
totalViolations: number
criticalViolations: number
highViolations: number
mediumViolations: number
lowViolations: number
categoryBreakdown: Record<string, number>
lastViolationAt: string | null
lastEscalationAt: string | null
sensitivityMultiplier: number
restrictions: {
suspended?: boolean
rateLimit?: number
queueForReview?: boolean
}
adminOverride: boolean
adminOverrideBy: string | null
adminOverrideAt: string | null
adminNotes: string | null
createdAt: string
updatedAt: string
}
export interface ThreatEscalationEvent {
id: string
userId: string
previousLevel: ThreatLevel
newLevel: ThreatLevel
previousScore: number
newScore: number
trigger: EscalationTrigger
triggerContentScoreId: string | null
metadata: Record<string, unknown>
createdAt: string
}
export interface ThreatLevelFilters {
page: number
limit: number
level?: ThreatLevel | ''
minScore?: number
maxScore?: number
}
export interface PaginatedThreatLevels {
items: UserThreatLevel[]
total: number
page: number
limit: number
pages: number
}