diff --git a/frontend/app/(protected)/dashboard/page.tsx b/frontend/app/(protected)/dashboard/page.tsx new file mode 100644 index 0000000..c06f86c --- /dev/null +++ b/frontend/app/(protected)/dashboard/page.tsx @@ -0,0 +1,286 @@ +'use client'; + +import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import Link from 'next/router'; +import { + FileText, ShieldAlert, Clock, ShieldCheck, + ArrowRight, UploadCloud, ArrowUpDown +} from 'lucide-react'; +import { + ResponsiveContainer, BarChart, Bar, XAxis, + YAxis, Tooltip, Cell +} from 'recharts'; + +interface Document { + id: string; + name: string; + status: 'PENDING' | 'ANALYZING' | 'VERIFIED' | 'FLAGGED' | 'REJECTED'; + riskScore: number; + createdAt: string; + reviewed?: boolean; +} + +export default function DashboardPage() { + const [documents, setDocuments] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [sortAsc, setSortAsc] = useState(false); + + // 1. Core Core REST Hydration Matrix + const fetchDashboardData = useCallback(async () => { + try { + const response = await fetch('/api/documents'); + if (response.ok) { + const data = await response.json(); + setDocuments(data); + } + } catch (error) { + console.error('Failed to resolve portfolio collection registries:', error); + } finally { + setIsLoading(false); + } + }, []); + + // 2. Real-Time WebSockets Sync Loop Integration (FE-06 Compliance) + useEffect(() => { + fetchDashboardData(); + + // Establish WebSocket stream pipeline link handles + const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const socket = new WebSocket(`${wsProtocol}//${window.location.host}/api/ws`); + + socket.onmessage = (event) => { + try { + const payload = JSON.parse(event.data); + // Intercept real-time update markers to trigger instant atomic UI synchronization + if (payload.type === 'documentStatus') { + fetchDashboardData(); + } + } catch (err) { + console.error('Failed to parse websocket synchronization frames:', err); + } + }; + + return () => socket.close(); + }, [fetchDashboardData]); + + // 3. Client-Side Data Aggregate Computations + const stats = useMemo(() => { + const total = documents.length; + if (total === 0) return { total: 0, avgRisk: 0, pending: 0, verified: 0 }; + + const sumRisk = documents.reduce((acc, doc) => acc + doc.riskScore, 0); + const pending = documents.filter(d => d.status === 'PENDING' || d.status === 'ANALYZING').length; + const verified = documents.filter(d => d.status === 'VERIFIED').length; + + return { + total, + avgRisk: Math.round(sumRisk / total), + pending, + verified + }; + }, [documents]); + + // 4. Transform Metrics for Chart.js / Recharts Engine Mapping + const chartData = useMemo(() => { + const counts = { PENDING: 0, VERIFIED: 0, FLAGGED: 0, REJECTED: 0 }; + documents.forEach(doc => { + const status = doc.status === 'ANALYZING' ? 'PENDING' : doc.status; + if (status in counts) counts[status as keyof typeof counts]++; + }); + + return [ + { name: 'Pending', count: counts.PENDING, color: '#f59e0b' }, + { name: 'Verified', count: counts.VERIFIED, color: '#10b981' }, + { name: 'Flagged', count: counts.FLAGGED, color: '#ef4444' }, + { name: 'Rejected', count: counts.REJECTED, color: '#64748b' }, + ]; + }, [documents]); + + // 5. Filter Actions & Sorting Realignment Operations + const flaggedActionItems = useMemo(() => { + return documents.filter(d => d.status === 'FLAGGED' && !d.reviewed); + }, [documents]); + + const recentDocuments = useMemo(() => { + return [...documents] + .sort((a, b) => { + const timeA = new Date(a.createdAt).getTime(); + const timeB = new Date(b.createdAt).getTime(); + return sortAsc ? timeA - timeB : timeB - timeA; + }) + .slice(0, 5); + }, [documents, sortAsc]); + + const getRiskColor = (score: number) => { + if (score <= 30) return 'text-emerald-500'; + if (score <= 60) return 'text-amber-500'; + return 'text-rose-500'; + }; + + if (isLoading) { + return ( +
+ Loading system dashboard... +
+ ); + } + + // EMPTY STATE FALLBACK PATH + if (documents.length === 0) { + return ( +
+
+ +
+

No documents tracking logs found

+

+ Upload your first verification vector target file to deploy active AI scoring metrics and lock secure ledger assets. +

+ +
+ ); + } + + return ( +
+ + {/* SECTION 1: STATS CARD ROW */} +
+
+
+ Total Document Base + {stats.total} +
+ +
+
+
+ Avg Portfolio Risk + + {stats.avgRisk}% + +
+ +
+
+
+ Pending Anchors + {stats.pending} +
+ +
+
+
+ Stellar Verified + {stats.verified} +
+ +
+
+ + {/* SECTION 2: CHARTS AND ACTIVE INCIDENT THREAT ACTION TRACKS */} +
+ + {/* Horizontal Distribution Engine */} +
+

Portfolio Risk Distribution

+
+ + + + + + + {chartData.map((entry, index) => ( + + ))} + + + +
+
+ + {/* Action Items List Card */} +
+
+

+ Attention Required +

+ {flaggedActionItems.length === 0 ? ( +

Zero unreviewed compliance threats reported.

+ ) : ( +
    + {flaggedActionItems.map(item => ( +
  • + {item.name} + + Risk: {item.riskScore}% + +
  • + ))} +
+ )} +
+
+
+ + {/* SECTION 3: RECENT ACTIVITY DOCUMENT LEDGER REPOSITORY */} +
+
+

Recent Documents Repository

+ +
+ +
+ + + + + + + + + + + + {recentDocuments.map(doc => ( + + + + + + + + ))} + +
Document NameLifecycle StatusThreat Vector Score setSortAsc(!sortAsc)}> +
+ Ingestion Date +
+
Actions
{doc.name} + + {doc.status} + + + {doc.riskScore}% + + {new Date(doc.createdAt).toLocaleDateString(undefined, { dateStyle: 'medium' })} + + + Inspect Detail + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/app/(protected)/onboarding/page.tsx b/frontend/app/(protected)/onboarding/page.tsx new file mode 100644 index 0000000..c90f6a5 --- /dev/null +++ b/frontend/app/(protected)/onboarding/page.tsx @@ -0,0 +1,304 @@ +'use client'; + +import React, { useState, useEffect, useCallback } from 'react'; +import { useRouter } from 'next/navigation'; +import { + Sparkles, ShieldCheck, UploadCloud, AlertCircle, + CheckCircle2, Clock, FileText, ChevronRight, ChevronLeft +} from 'lucide-react'; + +const ONBOARDING_STORAGE_KEY = 'smalda:onboarding:completed'; + +export default function OnboardingPage() { + const router = useRouter(); + const [currentStep, setCurrentStep] = useState(1); + const [isUploading, setIsUploading] = useState(false); + const [uploadError, setUploadError] = useState(null); + const [dragActive, setDragActive] = useState(false); + + // 1. Check eligibility criteria: show only for accounts < 24h old with 0 documents + useEffect(() => { + const checkOnboardingEligibility = async () => { + const isCompleted = localStorage.getItem(ONBOARDING_STORAGE_KEY); + if (isCompleted === 'true') { + router.replace('/dashboard'); + return; + } + + try { + // Fetch current user details and document counts from your API services + const [userRes, docsRes] = await Promise.all([ + fetch('/api/user/profile'), + fetch('/api/documents') + ]); + + if (userRes.ok && docsRes.ok) { + const user = await userRes.json(); + const docs = await docsRes.json(); + + const accountCreatedTime = new Date(user.createdAt).getTime(); + const oneDayInMs = 24 * 60 * 60 * 1000; + const isNewAccount = (Date.now() - accountCreatedTime) < oneDayInMs; + const hasZeroDocs = docs.length === 0; + + // Bypass onboarding if they aren't a new user or already have historical assets + if (!isNewAccount || !hasZeroDocs) { + completeOnboarding(); + } + } + } catch (err) { + console.error('Failed to verify onboarding eligibility barriers:', err); + } + }; + + checkOnboardingEligibility(); + }, [router]); + + const completeOnboarding = useCallback(() => { + localStorage.setItem(ONBOARDING_STORAGE_KEY, 'true'); + router.push('/dashboard'); + }, [router]); + + // 2. Step 2 Drag and Drop Event Interceptors + const handleDrag = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (e.type === "dragenter" || e.type === "dragover") { + setDragActive(true); + } else if (e.type === "dragleave") { + setDragActive(false); + } + }; + + const handleDrop = async (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + + if (e.dataTransfer.files && e.dataTransfer.files[0]) { + await uploadDocument(e.dataTransfer.files[0]); + } + }; + + const handleFileInput = async (e: React.ChangeEvent) => { + if (e.target.files && e.target.files[0]) { + await uploadDocument(e.target.files[0]); + } + }; + + // 3. API Upload Connection + const uploadDocument = async (file: File) => { + setIsUploading(true); + setUploadError(null); + + const formData = new FormData(); + formData.append('file', file); + + try { + const response = await fetch('/api/documents/upload', { + method: 'POST', + body: formData, + }); + + if (!response.ok) throw new Error('Upload transaction declined by endpoint.'); + + // Advance to next step once file is processed + setCurrentStep(3); + } catch (err: any) { + setUploadError(err.message || 'Something went wrong during ingestion.'); + } finally { + setIsUploading(false); + } + }; + + return ( +
+ {/* Upper Progress Banner Header */} +
+
+ + SMALDA Onboarding +
+ +
+ + {/* Main Multi-step Sliding Track Window */} +
+ + {/* STEP 1: WELCOME INFO */} + {currentStep === 1 && ( +
+

Welcome to SMALDA

+

+ SMALDA bridges the gap between documents and secure evaluation data metrics. Let's unpack the core underlying engine pipelines in plain language: +

+
+
+ +
+

Automated AI Risk Scoring

+

Our engine scans uploaded documents for validation anomalies, assigning contextual threat markers automatically.

+
+
+
+ +
+

Stellar Blockchain Verification

+

A unique cryptographic fingerprint of your file is anchored onto the Stellar ledger to lock proof-of-authenticity permanently.

+
+
+
+
+ )} + + {/* STEP 2: DRAG & DROP UPLOAD */} + {currentStep === 2 && ( +
+
+

Upload Your First File

+

Kickstart system tracking by submitting an analysis target document.

+
+ +
+ + + +

or drag and drop your file boundary box here

+ + {uploadError && ( +
+ + {uploadError} +
+ )} +
+
+ )} + + {/* STEP 3: STATUS MAP DEFINITIONS */} + {currentStep === 3 && ( +
+
+

Understanding Tracking Statuses

+

Here is how to monitor document evaluation files across their lifecycle pipelines:

+
+ +
+
+ +
+ PENDING + File is securely queued. +
+
+
+ +
+ ANALYZING + AI engines evaluating file parameters. +
+
+
+ +
+ VERIFIED + Cryptographic proof verified on Stellar. +
+
+
+ +
+ FLAGGED + Risk indicators exceeded threshold lines. +
+
+
+
+ )} + + {/* STEP 4: COMPLETE */} + {currentStep === 4 && ( +
+
+ +
+

Setup Complete!

+

+ Your tracking onboarding is finished. You are ready to open your analytics command matrix dashboard. +

+
+ )} +
+ + {/* Footer Controls & Navigation Step Gauges */} + +
+ ); +} \ No newline at end of file