import React, { useState, useEffect } from 'react'; import { Header } from './components/Header'; import { Footer } from './components/Footer'; import { RulebookSelector } from './components/RulebookSelector'; import { FilingWorkspace } from './components/FilingWorkspace'; import { MyFilingsDashboard } from './components/MyFilingsDashboard'; import { RulebooksModal } from './components/RulebooksModal'; import { AppealTrackerModal } from './components/AppealTrackerModal'; import { RevisionHistoryModal } from './components/RevisionHistoryModal'; import { ShareModal } from './components/ShareModal'; import { PDFExportModal } from './components/PDFExportModal'; import { LegalInfoModal } from './components/LegalInfoModal'; import { GuidedIntakeModal } from './components/GuidedIntakeModal'; import { RtiAuditorModal } from './components/RtiAuditorModal'; import { INITIAL_CASES, INITIAL_RULEBOOKS } from './data/mockData'; import { LegalCase, Rulebook, ExtractedFact } from './types'; import { AIAnalysisResponse } from './services/api'; import { useAuth } from './context/AuthContext'; import { subscribeToUserCases, saveUserCaseToFirestore, deleteUserCaseFromFirestore, } from './firebase'; export default function App() { const { user } = useAuth(); const [currentView, setCurrentView] = useState<'home' | 'workspace' | 'my-filings'>('home'); const [cases, setCases] = useState(INITIAL_CASES); const [selectedCaseId, setSelectedCaseId] = useState(INITIAL_CASES[0].id); const [rulebooks] = useState(INITIAL_RULEBOOKS); const [selectedCity, setSelectedCity] = useState('Delhi (NCR)'); const [toastMessage, setToastMessage] = useState(null); // Modals state const [showRulebooksModal, setShowRulebooksModal] = useState(false); const [showAppealTracker, setShowAppealTracker] = useState(false); const [showRevisionHistory, setShowRevisionHistory] = useState(false); const [showGuidedIntake, setShowGuidedIntake] = useState(false); const [guidedIntakeCategory, setGuidedIntakeCategory] = useState(undefined); const [showRtiAuditor, setShowRtiAuditor] = useState(false); const [caseToShare, setCaseToShare] = useState(null); const [caseToPDF, setCaseToPDF] = useState(null); const [legalModalType, setLegalModalType] = useState< 'disclaimer' | 'accessibility' | 'rulebooks' | 'privacy' | null >(null); const showToast = (msg: string) => { setToastMessage(msg); setTimeout(() => setToastMessage(null), 3000); }; // Real-time Firestore synchronization when user is signed in useEffect(() => { if (!user) { // If user logs out or is guest, retain in-memory state return; } // Subscribe to Firestore cases subcollection const unsubscribe = subscribeToUserCases( user.uid, (remoteCases) => { setCases(remoteCases); if (remoteCases.length > 0) { if (!remoteCases.some((c) => c.id === selectedCaseId)) { setSelectedCaseId(remoteCases[0].id); } } }, (err) => { console.error('Real-time sync listener error:', err); } ); return () => unsubscribe(); }, [user]); const currentCase = cases.find((c) => c.id === selectedCaseId) || cases[0] || INITIAL_CASES[0]; const handleUpdateCase = (updatedCase: LegalCase) => { setCases((prev) => prev.map((c) => (c.id === updatedCase.id ? updatedCase : c))); if (user) { saveUserCaseToFirestore(user.uid, updatedCase).catch((err) => console.error('Failed to sync updated case to Firestore:', err) ); } }; const handleSelectCase = (caseId: string) => { setSelectedCaseId(caseId); setCurrentView('workspace'); }; const handleLaunchAICase = (aiData: AIAnalysisResponse, rawText: string) => { const newCaseId = `case-${Date.now()}`; const formattedFacts: ExtractedFact[] = aiData.facts.map((f, idx) => ({ id: `fact-${Date.now()}-${idx}`, label: f.label, value: f.value, })); const formattedPii = (aiData.piiItems || []).map((p, idx) => ({ id: `pii-${Date.now()}-${idx}`, original: p.original, type: p.type, masked: true, })); const todayStr = new Intl.DateTimeFormat('en-IN', { month: 'long', day: 'numeric', year: 'numeric', }).format(new Date()); const newCase: LegalCase = { id: newCaseId, caseNumber: `#CL-${new Date().getFullYear()}-${Math.floor(1000 + Math.random() * 9000)}`, title: aiData.title, category: aiData.category, rulebookId: aiData.rulebookId, status: 'drafting', statusBadge: 'Assessed', statusType: 'success', deadlineText: `Reply required in ${aiData.daysRemaining} days`, filedDate: todayStr, description: aiData.formalLetter || aiData.legalDiagnosis, formalLetter: aiData.formalLetter, rawIntakeText: rawText, redactedText: aiData.redactedText, piiItems: formattedPii.length > 0 ? formattedPii : [{ id: `pii-${Date.now()}-1`, original: 'Citizen Requester', type: 'Identity', masked: true }], facts: formattedFacts.length > 0 ? formattedFacts : [{ id: `fact-${Date.now()}-1`, label: 'Primary Grievance', value: rawText }], officer: { name: aiData.officer.name, title: aiData.officer.title, department: aiData.officer.department, avatar: aiData.officer.avatar || 'PO', jurisdiction: aiData.officer.jurisdiction || selectedCity, email: aiData.officer.email, address: aiData.officer.address, portalUrl: aiData.officer.portalUrl, designationNote: aiData.officer.designationNote, }, requestScope: aiData.requestScope, dateRange: aiData.dateRange || 'Past 90 Days', statute: aiData.statute, score: aiData.initialScore || 85, hasHardshipClause: false, autoRedactEnabled: true, autoEscalateEnabled: true, daysRemaining: aiData.daysRemaining || 18, revisionCount: 1, lastEdited: 'Just now', vulnerabilities: aiData.vulnerabilities, recommendedFixes: aiData.recommendedFixes, appellateStrategy: aiData.appellateStrategy, statutoryClauses: aiData.statutoryClauses, recommendedExhibits: aiData.recommendedExhibits, }; setCases((prev) => [newCase, ...prev]); setSelectedCaseId(newCaseId); if (user) { saveUserCaseToFirestore(user.uid, newCase) .then(() => showToast('Case saved and synced to your Firebase Cloud')) .catch((err) => console.error('Cloud save error:', err)); } setCurrentView('workspace'); }; const handleLaunchCustomDraft = (draftText: string, caseTitle: string) => { const newCaseId = `case-${Date.now()}`; const todayStr = new Intl.DateTimeFormat('en-IN', { month: 'long', day: 'numeric', year: 'numeric', }).format(new Date()); const newCase: LegalCase = { id: newCaseId, caseNumber: `#CL-${new Date().getFullYear()}-${Math.floor(1000 + Math.random() * 9000)}`, title: caseTitle || 'Right to Information Notice', category: 'Public Records (RTI)', rulebookId: 'rti', status: 'drafting', statusBadge: 'RTI Audited', statusType: 'success', deadlineText: 'Reply required in 30 days', filedDate: todayStr, description: draftText, formalLetter: draftText, rawIntakeText: draftText, redactedText: draftText, piiItems: [], facts: [{ id: `fact-1`, label: 'RTI Scope', value: 'Statutory inspection & certified records request.' }], officer: { name: 'Public Information Officer (PIO)', title: 'Designated CPIO / PIO', department: 'Public Authority Division', avatar: 'PIO', jurisdiction: selectedCity, email: 'rti.cell@nic.in', address: `${selectedCity} Central Government / Civic Complex`, portalUrl: 'https://rtionline.gov.in', }, requestScope: 'Certified copies under Section 6(1) and Section 2(j) of RTI Act 2005.', dateRange: 'Relevant Financial Year', statute: 'Right to Information Act 2005 § 6(1)', score: 95, hasHardshipClause: false, autoRedactEnabled: true, autoEscalateEnabled: true, daysRemaining: 30, revisionCount: 1, lastEdited: 'Just now', }; setCases((prev) => [newCase, ...prev]); setSelectedCaseId(newCaseId); if (user) { saveUserCaseToFirestore(user.uid, newCase) .then(() => showToast('RTI Filing saved to Firebase Firestore')) .catch((err) => console.error('Cloud save error:', err)); } setCurrentView('workspace'); }; const handleSaveAICaseToCloud = async (aiData: AIAnalysisResponse, rawText: string) => { const newCaseId = `case-${Date.now()}`; const formattedFacts: ExtractedFact[] = aiData.facts.map((f, idx) => ({ id: `fact-${Date.now()}-${idx}`, label: f.label, value: f.value, })); const formattedPii = (aiData.piiItems || []).map((p, idx) => ({ id: `pii-${Date.now()}-${idx}`, original: p.original, type: p.type, masked: true, })); const todayStr = new Intl.DateTimeFormat('en-IN', { month: 'long', day: 'numeric', year: 'numeric', }).format(new Date()); const newCase: LegalCase = { id: newCaseId, caseNumber: `#CL-${new Date().getFullYear()}-${Math.floor(1000 + Math.random() * 9000)}`, title: aiData.title, category: aiData.category, rulebookId: aiData.rulebookId, status: 'drafting', statusBadge: 'Assessed', statusType: 'success', deadlineText: `Reply required in ${aiData.daysRemaining} days`, filedDate: todayStr, description: aiData.formalLetter || aiData.legalDiagnosis, formalLetter: aiData.formalLetter, rawIntakeText: rawText, redactedText: aiData.redactedText, piiItems: formattedPii, facts: formattedFacts, officer: { name: aiData.officer.name, title: aiData.officer.title, department: aiData.officer.department, avatar: aiData.officer.avatar || 'PO', jurisdiction: aiData.officer.jurisdiction || selectedCity, email: aiData.officer.email, address: aiData.officer.address, portalUrl: aiData.officer.portalUrl, designationNote: aiData.officer.designationNote, }, requestScope: aiData.requestScope, dateRange: aiData.dateRange || 'Past 90 Days', statute: aiData.statute, score: aiData.initialScore || 85, hasHardshipClause: false, autoRedactEnabled: true, autoEscalateEnabled: true, daysRemaining: aiData.daysRemaining || 18, revisionCount: 1, lastEdited: 'Just now', vulnerabilities: aiData.vulnerabilities, recommendedFixes: aiData.recommendedFixes, appellateStrategy: aiData.appellateStrategy, statutoryClauses: aiData.statutoryClauses, recommendedExhibits: aiData.recommendedExhibits, }; setCases((prev) => [newCase, ...prev]); if (user) { await saveUserCaseToFirestore(user.uid, newCase); showToast('Filing saved to Firebase Firestore Cloud'); } else { showToast('Saved locally. Sign in to enable permanent cloud backup.'); } }; const handleSelectRulebook = (rulebookId: string, initialPrompt?: string) => { let title = 'New Filing Draft'; let category = 'Public Records (RTI)'; let scope = 'Certified copies of files, tender details, and official note sheets.'; let statute = 'Right to Information Act 2005 § 6(1)'; if (rulebookId === 'consumer') { title = 'Consumer Dispute - Defective Product & Deficient Service'; category = 'Consumer Disputes'; scope = 'Formal 15-day statutory notice for full refund and compensation.'; statute = 'Consumer Protection Act 2019 § 35'; } else if (rulebookId === 'tenancy') { title = 'Tenancy Dispute - Security Deposit Recovery'; category = 'Tenancy Protections'; scope = 'Statutory notice demanding immediate return of withheld security deposit with interest.'; statute = 'State Tenancy & Rent Control Act'; } else if (rulebookId === 'employment') { title = 'Wage Recovery & Final Settlement Demand'; category = 'Labor & Employment Law'; scope = 'Demand for immediate release of unpaid salary, F&F settlement, and relieving certificate.'; statute = 'Payment of Wages Act 1936 § 15'; } const newCaseId = `case-${Date.now()}`; const newCase: LegalCase = { ...currentCase, id: newCaseId, caseNumber: `#CL-2025-${Math.floor(1000 + Math.random() * 9000)}`, title: initialPrompt ? `Case: ${initialPrompt.slice(0, 45)}...` : title, category, rulebookId, requestScope: initialPrompt ? initialPrompt : scope, statute, filedDate: 'Current Filing', status: 'drafting', statusBadge: 'In Progress', statusType: 'warning', score: 85, hasHardshipClause: false, autoRedactEnabled: true, autoEscalateEnabled: false, daysRemaining: 30, lastEdited: 'Just now', }; setCases((prev) => [newCase, ...prev]); setSelectedCaseId(newCaseId); if (user) { saveUserCaseToFirestore(user.uid, newCase).catch((err) => console.error('Error saving template case to cloud:', err) ); } setCurrentView('workspace'); }; const handleDeleteCase = (caseId: string) => { setCases((prev) => prev.filter((c) => c.id !== caseId)); if (user) { deleteUserCaseFromFirestore(user.uid, caseId) .then(() => showToast('Case filing deleted from cloud')) .catch((err) => console.error('Error deleting from Firestore:', err)); } else { showToast('Case removed'); } }; const handleDuplicateCase = (legalCase: LegalCase) => { const duplicatedId = `case-${Date.now()}`; const duplicatedCase: LegalCase = { ...legalCase, id: duplicatedId, caseNumber: `#CL-${new Date().getFullYear()}-${Math.floor(1000 + Math.random() * 9000)}`, title: `${legalCase.title} (Copy)`, lastEdited: 'Just now', revisionCount: 1, }; setCases((prev) => [duplicatedCase, ...prev]); if (user) { saveUserCaseToFirestore(user.uid, duplicatedCase) .then(() => showToast('Case duplicated and saved in Cloud')) .catch((err) => console.error('Error saving duplicate to Firestore:', err)); } else { showToast('Case duplicated'); } }; const handleNewFiling = () => { setShowGuidedIntake(true); }; const handleOpenGuidedIntakeWithCategory = (category?: string) => { setGuidedIntakeCategory(category); setShowGuidedIntake(true); }; const handleDownloadPDF = (legalCase: LegalCase) => { setCaseToPDF(legalCase); }; const handleShareCase = (legalCase: LegalCase) => { setCaseToShare(legalCase); }; const handleOpenQuickAction = (action: 'appeal' | 'history' | 'rulebooks') => { if (action === 'appeal') setShowAppealTracker(true); if (action === 'history') setShowRevisionHistory(true); if (action === 'rulebooks') setShowRulebooksModal(true); }; return (
{/* Top Notification Toast */} {toastMessage && (
check_circle {toastMessage}
)} {/* TopNavBar Header */}
{/* Main View Router */}
{currentView === 'home' && ( setShowRulebooksModal(true)} onAnalyzePrompt={() => {}} onOpenGuidedIntake={handleOpenGuidedIntakeWithCategory} onOpenRtiAuditor={() => setShowRtiAuditor(true)} /> )} {currentView === 'workspace' && ( )} {currentView === 'my-filings' && ( )}
{/* Footer */}
); }