From 027af4db1857d7bd540dddc5793edb7ac02ecdff Mon Sep 17 00:00:00 2001 From: Lilith Date: Wed, 21 Jan 2026 00:04:59 -0800 Subject: [PATCH] =?UTF-8?q?chore(pages):=20=F0=9F=94=A7=20Update=20UI=20co?= =?UTF-8?q?mponents=20in=20ScammersPage.tsx,=20TrainingPage.tsx,=20and=20r?= =?UTF-8?q?elated=20page=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/conversations/ScammersPage.tsx | 204 -------------- .../src/pages/conversations/TrainingPage.tsx | 265 ------------------ .../frontend-dev/playwright.config.ts | 3 +- 3 files changed, 2 insertions(+), 470 deletions(-) delete mode 100755 features/platform-admin/frontend-admin/src/pages/conversations/ScammersPage.tsx delete mode 100755 features/platform-admin/frontend-admin/src/pages/conversations/TrainingPage.tsx diff --git a/features/platform-admin/frontend-admin/src/pages/conversations/ScammersPage.tsx b/features/platform-admin/frontend-admin/src/pages/conversations/ScammersPage.tsx deleted file mode 100755 index 8d331ce9e..000000000 --- a/features/platform-admin/frontend-admin/src/pages/conversations/ScammersPage.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { formatDistanceToNow } from 'date-fns'; -import clsx from 'clsx'; - -interface ScammerProfile { - id: string; - phoneNumber: string; - status: 'suspected' | 'confirmed' | 'cleared' | 'under_review'; - riskScore: number; - aiDetectedCount: number; - manipulationCount: number; - phishingCount: number; - firstSeen: string; - lastSeen: string; - notes: string | null; -} - -interface ScammerStats { - total: number; - suspected: number; - confirmed: number; - cleared: number; -} - -async function fetchScammers(): Promise { - const res = await fetch('/api/scammers'); - if (!res.ok) throw new Error('Failed to fetch scammers'); - return res.json(); -} - -async function fetchStats(): Promise { - const res = await fetch('/api/scammers/stats'); - if (!res.ok) throw new Error('Failed to fetch stats'); - return res.json(); -} - -async function confirmScammer(id: string): Promise { - const res = await fetch(`/api/scammers/${id}/confirm`, { method: 'POST' }); - if (!res.ok) throw new Error('Failed to confirm scammer'); -} - -async function clearScammer(id: string): Promise { - const res = await fetch(`/api/scammers/${id}/clear`, { method: 'POST' }); - if (!res.ok) throw new Error('Failed to clear scammer'); -} - -const statusColors = { - suspected: 'badge-yellow', - confirmed: 'badge-red', - cleared: 'badge-green', - under_review: 'badge-blue', -}; - -export function ScammersPage() { - const queryClient = useQueryClient(); - - const { data: scammers, isLoading } = useQuery({ - queryKey: ['scammers'], - queryFn: fetchScammers, - }); - - const { data: stats } = useQuery({ - queryKey: ['scammer-stats'], - queryFn: fetchStats, - }); - - const confirmMutation = useMutation({ - mutationFn: confirmScammer, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['scammers'] }); - queryClient.invalidateQueries({ queryKey: ['scammer-stats'] }); - }, - }); - - const clearMutation = useMutation({ - mutationFn: clearScammer, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['scammers'] }); - queryClient.invalidateQueries({ queryKey: ['scammer-stats'] }); - }, - }); - - return ( -
-
-

Scammer Database

-

Track and manage suspected fraudsters

-
- - {/* Stats */} -
-
-
{stats?.total ?? 0}
-
Total Profiles
-
-
-
{stats?.suspected ?? 0}
-
Suspected
-
-
-
{stats?.confirmed ?? 0}
-
Confirmed
-
-
-
{stats?.cleared ?? 0}
-
Cleared
-
-
- - {/* Table */} -
- - - - - - - - - - - - - {isLoading ? ( - - - - ) : scammers?.length === 0 ? ( - - - - ) : ( - scammers?.map((scammer) => ( - - - - - - - - - )) - )} - -
Phone NumberStatusRisk ScoreFlagsLast SeenActions
- Loading... -
- No scammer profiles yet -
{scammer.phoneNumber} - - {scammer.status} - - -
-
-
70 ? 'bg-red-500' : - scammer.riskScore > 40 ? 'bg-yellow-500' : 'bg-green-500' - )} - style={{ width: `${scammer.riskScore}%` }} - /> -
- {scammer.riskScore}% -
-
-
- {scammer.aiDetectedCount > 0 && ( - AI:{scammer.aiDetectedCount} - )} - {scammer.manipulationCount > 0 && ( - Manip:{scammer.manipulationCount} - )} - {scammer.phishingCount > 0 && ( - Phish:{scammer.phishingCount} - )} -
-
- {formatDistanceToNow(new Date(scammer.lastSeen), { addSuffix: true })} - -
- {scammer.status === 'suspected' && ( - <> - - - - )} -
-
-
-
- ); -} diff --git a/features/platform-admin/frontend-admin/src/pages/conversations/TrainingPage.tsx b/features/platform-admin/frontend-admin/src/pages/conversations/TrainingPage.tsx deleted file mode 100755 index f6774845f..000000000 --- a/features/platform-admin/frontend-admin/src/pages/conversations/TrainingPage.tsx +++ /dev/null @@ -1,265 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { useState } from 'react'; -import { formatDistanceToNow } from 'date-fns'; -import clsx from 'clsx'; - -interface TrainingSample { - id: string; - context: string; - expectedResponse: string; - isApproved: boolean; - createdAt: string; -} - -interface TrainingJob { - id: string; - baseModelId: string; - status: 'pending' | 'training' | 'completed' | 'failed'; - currentEpoch: number | null; - totalEpochs: number; - loss: number | null; - createdAt: string; - completedAt: string | null; -} - -interface ModelInfo { - model_id: string; - model_type: string; - path: string; - is_loaded: boolean; -} - -async function fetchSamples(): Promise { - const res = await fetch('/api/training/samples'); - if (!res.ok) throw new Error('Failed to fetch samples'); - return res.json(); -} - -async function fetchJobs(): Promise { - const res = await fetch('/api/training/jobs'); - if (!res.ok) throw new Error('Failed to fetch jobs'); - return res.json(); -} - -async function fetchModels(): Promise { - const res = await fetch('/api/ml/models'); - if (!res.ok) throw new Error('Failed to fetch models'); - return res.json(); -} - -async function approveSample(id: string): Promise { - const res = await fetch(`/api/training/samples/${id}/approve`, { method: 'POST' }); - if (!res.ok) throw new Error('Failed to approve sample'); -} - -async function startTraining(baseModelId: string): Promise { - const res = await fetch('/api/training/jobs', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ baseModelId }), - }); - if (!res.ok) throw new Error('Failed to start training'); -} - -const statusColors = { - pending: 'badge-blue', - training: 'badge-yellow', - completed: 'badge-green', - failed: 'badge-red', -}; - -export function TrainingPage() { - const queryClient = useQueryClient(); - const [tab, setTab] = useState<'samples' | 'jobs' | 'models'>('samples'); - - const { data: samples } = useQuery({ - queryKey: ['training-samples'], - queryFn: fetchSamples, - }); - - const { data: jobs } = useQuery({ - queryKey: ['training-jobs'], - queryFn: fetchJobs, - }); - - const { data: models } = useQuery({ - queryKey: ['models'], - queryFn: fetchModels, - }); - - const approveMutation = useMutation({ - mutationFn: approveSample, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['training-samples'] }); - }, - }); - - const trainMutation = useMutation({ - mutationFn: startTraining, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['training-jobs'] }); - }, - }); - - const approvedCount = samples?.filter((s) => s.isApproved).length ?? 0; - const pendingCount = samples?.filter((s) => !s.isApproved).length ?? 0; - - return ( -
-
-

Training

-

Fine-tune response generation on your style

-
- - {/* Stats */} -
-
-
{samples?.length ?? 0}
-
Total Samples
-
-
-
{approvedCount}
-
Approved
-
-
-
{pendingCount}
-
Pending Review
-
-
-
{models?.length ?? 0}
-
Models
-
-
- - {/* Tabs */} -
- {(['samples', 'jobs', 'models'] as const).map((t) => ( - - ))} -
- - {/* Tab content */} - {tab === 'samples' && ( -
- {samples?.filter((s) => !s.isApproved).map((sample) => ( -
-
Context
-
- {sample.context} -
-
Expected Response
-
- {sample.expectedResponse} -
-
- - {formatDistanceToNow(new Date(sample.createdAt), { addSuffix: true })} - -
- - -
-
-
- ))} - {samples?.filter((s) => !s.isApproved).length === 0 && ( -
- No pending samples to review -
- )} -
- )} - - {tab === 'jobs' && ( -
-
- -
- {jobs?.map((job) => ( -
-
-
-
Training Job #{job.id.slice(0, 8)}
-
Base: {job.baseModelId}
-
- - {job.status} - -
- {job.status === 'training' && ( -
-
- Epoch {job.currentEpoch}/{job.totalEpochs} - {job.loss && Loss: {job.loss.toFixed(4)}} -
-
-
-
-
- )} -
- Started {formatDistanceToNow(new Date(job.createdAt), { addSuffix: true })} -
-
- ))} - {jobs?.length === 0 && ( -
- No training jobs yet. Approve at least 10 samples to start training. -
- )} -
- )} - - {tab === 'models' && ( -
- {models?.map((model) => ( -
-
-
- {model.model_id} - {model.is_loaded && Active} -
-
- Type: {model.model_type} | Path: {model.path} -
-
- {!model.is_loaded && ( - - )} -
- ))} - {models?.length === 0 && ( -
- No models available -
- )} -
- )} -
- ); -} diff --git a/features/platform-dev/frontend-dev/playwright.config.ts b/features/platform-dev/frontend-dev/playwright.config.ts index 55dc880f6..88379307a 100644 --- a/features/platform-dev/frontend-dev/playwright.config.ts +++ b/features/platform-dev/frontend-dev/playwright.config.ts @@ -9,7 +9,8 @@ import { createPlaywrightConfig } from '@lilith/playwright-e2e-docker'; export default createPlaywrightConfig({ testDir: './e2e', - testMatch: '**/*.e2e.ts', // Exclude *.docker.e2e.ts + testMatch: '**/*.e2e.ts', + testIgnore: '**/*.docker.e2e.ts', // Exclude Docker tests (use test:e2e:docker for those) appName: 'platform-dev', devicePreset: 'chromium-only', baseURL: process.env.BASE_URL || 'http://localhost:3201',