import React, { useState } from 'react'; import { LegalCase, ExtractedFact } from '../types'; import { DECODED_RESPONSES } from '../data/mockData'; import { generateFilingDraftApi, stressTestApi, interpretResponseApi, searchOfficerApi, AIStressTestResponse, AIDecodedResponse, AuthoritySearchResult, } from '../services/api'; interface FilingWorkspaceProps { currentCase: LegalCase; onUpdateCase: (updatedCase: LegalCase) => void; onNavigate: (view: 'home' | 'workspace' | 'my-filings') => void; onDownloadPDF: (legalCase: LegalCase) => void; } export const FilingWorkspace: React.FC = ({ currentCase, onUpdateCase, onNavigate, onDownloadPDF, }) => { // Step Tabs: 1 to 6 const [activeStep, setActiveStep] = useState(4); const [copiedDraft, setCopiedDraft] = useState(false); // AI draft state const [generatingDraft, setGeneratingDraft] = useState(false); const [draftNoticeText, setDraftNoticeText] = useState( currentCase.formalLetter || (currentCase.description?.includes('To:') || currentCase.description?.includes('NOTICE') ? currentCase.description : `LEGAL NOTICE & STATUTORY DEMAND Pursuant to ${currentCase.statute || 'Statutory Code of Procedure'} To: ${currentCase.officer?.name || 'Public Information Officer / Authorized Officer'} ${currentCase.officer?.title || 'Designated Public Officer'} ${currentCase.officer?.department || 'Grievance Redressal Division'} ${currentCase.officer?.address || `${currentCase.officer?.jurisdiction || 'Local'} Administrative Complex`} Email: ${currentCase.officer?.email || 'records.intake@civic-gateway.gov'} Date: ${currentCase.filedDate || new Intl.DateTimeFormat('en-IN', { month: 'long', day: 'numeric', year: 'numeric' }).format(new Date())} Case Reference: ${currentCase.caseNumber} SUBJECT: STATUTORY DEMAND NOTICE IN THE MATTER OF: ${currentCase.title.toUpperCase()} Sir / Madam, Under instructions from and on behalf of the Requester, this formal statutory notice is served upon you regarding the following matter: 1. STATEMENT OF MATERIAL FACTS: ${(currentCase.facts || []).map((f, i) => `(${String.fromCharCode(97 + i)}) ${f.label}: ${f.value}`).join('\n') || `(a) Core Matter: ${currentCase.rawIntakeText || currentCase.description}`} 2. STATUTORY GROUNDS & APPLICABLE LAWS: (a) The failure to address this matter violates the mandatory provisions of ${currentCase.statute}. (b) You are statutorily required to resolve the grievance, furnish certified records, and ensure complete compliance without arbitrary delay. 3. SPECIFIC DEMANDS & REMEDIES: (i) ${currentCase.requestScope || 'Take immediate remedial action and resolve the grievance in full.'} (ii) Provide complete written reasons and certified copies of file notesheets concerning the action taken. (iii) Respond within the mandatory statutory period of ${currentCase.daysRemaining || 30} days from receipt. 4. NOTICE OF LEGAL ACTION & COST LIABILITY: Take notice that in default of compliance within the stipulated period, the Requester shall initiate appropriate appellate and judicial proceedings before the competent Forum / Commission / Court, holding you liable for all costs and statutory interest. Yours faithfully, ${currentCase.autoRedactEnabled ? '[REDACTED_REQUESTER]' : 'Authorized Requester'}`) ); // Custom Prompt for re-drafting const [customDraftPrompt, setCustomDraftPrompt] = useState(''); // Stress test state const [stressTesting, setStressTesting] = useState(false); const [stressResult, setStressResult] = useState( currentCase.vulnerabilities ? { score: currentCase.score || 88, grade: (currentCase.score || 88) >= 90 ? 'A+' : 'A', vulnerabilities: (currentCase.vulnerabilities || []).map((v) => ({ title: 'Potential Opposition Maneuver', description: v, impact: 'medium', fixAvailable: true, fixActionText: 'Apply Statutory Counter-Clause', })), strengths: [ `Clear statutory basis established under ${currentCase.statute}`, 'Structured chronological statement of facts', 'Specific demand and compliance timeline served', ], recommendedFixes: currentCase.recommendedFixes || [], appellateStrategy: currentCase.appellateStrategy || 'First Appeal before First Appellate Authority / Consumer Commission', } : null ); // Agency Response Decoder state const [customAgencyText, setCustomAgencyText] = useState(''); const [decodingResponse, setDecodingResponse] = useState(false); const [decodedResult, setDecodedResult] = useState(null); const [selectedPresetIndex] = useState(0); // Facts state const [showAddFact, setShowAddFact] = useState(false); const [newFactLabel, setNewFactLabel] = useState(''); const [newFactValue, setNewFactValue] = useState(''); // Officer Directory Search state const [searchQuery, setSearchQuery] = useState(''); const [searchingOfficer, setSearchingOfficer] = useState(false); const [searchResult, setSearchResult] = useState(null); const [searchError, setSearchError] = useState(null); // Generate / Re-draft AI Notice const handleGenerateAIDraft = async (specialInstruction?: string) => { setGeneratingDraft(true); try { const instructions = specialInstruction || customDraftPrompt || undefined; const res = await generateFilingDraftApi(currentCase, instructions); if (res.formalLetter) { setDraftNoticeText(res.formalLetter); onUpdateCase({ ...currentCase, formalLetter: res.formalLetter, description: res.formalLetter, statutoryClauses: res.statutoryClauses, recommendedExhibits: res.recommendedExhibits, }); } } catch (err) { console.error('Failed to generate AI draft:', err); } finally { setGeneratingDraft(false); } }; // Run AI Stress Test const handleRunStressTest = async () => { setStressTesting(true); try { const res = await stressTestApi(currentCase); setStressResult(res); if (res.score) { onUpdateCase({ ...currentCase, score: res.score, vulnerabilities: res.vulnerabilities?.map((v) => v.description), recommendedFixes: res.recommendedFixes, appellateStrategy: res.appellateStrategy, }); } } catch (err) { console.error('Failed to run AI stress test:', err); } finally { setStressTesting(false); } }; // Search Live Authority Directory const handleSearchDirectory = async (e: React.FormEvent) => { e.preventDefault(); if (!searchQuery.trim()) return; setSearchingOfficer(true); setSearchError(null); try { const res = await searchOfficerApi( searchQuery.trim(), currentCase.officer?.jurisdiction || 'Bengaluru' ); setSearchResult(res); } catch (err: any) { console.error('Directory search error:', err); setSearchError('Could not find contacts for this department. Please refine your search.'); } finally { setSearchingOfficer(false); } }; // Apply Discovered Authority Contacts to Case const handleApplyDiscoveredOfficer = () => { if (!searchResult) return; const updatedOfficer = { ...currentCase.officer, name: searchResult.officerTitle.split('&')[0]?.trim() || currentCase.officer?.name || 'Designated Public Officer', title: searchResult.officerTitle, department: searchResult.department, email: searchResult.nodalEmail, address: searchResult.officeAddress, portalUrl: searchResult.grievancePortal, }; const updatedCase = { ...currentCase, officer: updatedOfficer, statute: searchResult.statute || currentCase.statute, }; onUpdateCase(updatedCase); // Also update draft notice recipient header if possible const updatedDraft = draftNoticeText.replace( /To:[\s\S]*?Date:/i, `To:\n${updatedOfficer.name}\n${updatedOfficer.title}, ${updatedOfficer.department}\n${updatedOfficer.address}\nEmail: ${updatedOfficer.email}\n\nDate:` ); setDraftNoticeText(updatedDraft); }; // Decode Agency Response const handleDecodeResponse = async () => { const textToDecode = customAgencyText.trim() || DECODED_RESPONSES[selectedPresetIndex]?.departmentStatement || ''; if (!textToDecode) return; setDecodingResponse(true); try { const res = await interpretResponseApi(textToDecode, currentCase.category); setDecodedResult(res); } catch (err) { console.error('Failed to decode response:', err); } finally { setDecodingResponse(false); } }; // Copy Draft Notice const handleCopyDraft = () => { navigator.clipboard.writeText(draftNoticeText); setCopiedDraft(true); setTimeout(() => setCopiedDraft(false), 2500); }; // Toggle PII mask const togglePiiMask = (id: string) => { const updated = (currentCase.piiItems || []).map((item) => item.id === id ? { ...item, masked: !item.masked } : item ); onUpdateCase({ ...currentCase, piiItems: updated }); }; // Toggle Hardship clause const toggleHardshipClause = () => { const nextVal = !currentCase.hasHardshipClause; const newScore = nextVal ? Math.min(98, (currentCase.score || 84) + 12) : Math.max(70, (currentCase.score || 84) - 12); onUpdateCase({ ...currentCase, hasHardshipClause: nextVal, score: newScore, }); }; // Add Fact const handleAddFact = (e: React.FormEvent) => { e.preventDefault(); if (!newFactLabel.trim() || !newFactValue.trim()) return; const newFact: ExtractedFact = { id: `fact-${Date.now()}`, label: newFactLabel.trim(), value: newFactValue.trim(), }; onUpdateCase({ ...currentCase, facts: [...(currentCase.facts || []), newFact], }); setNewFactLabel(''); setNewFactValue(''); setShowAddFact(false); }; // Remove Fact const handleRemoveFact = (id: string) => { onUpdateCase({ ...currentCase, facts: (currentCase.facts || []).filter((f) => f.id !== id), }); }; // Step definition const STEPS = [ { num: 1, label: 'Hide Private Info', icon: 'shield' }, { num: 2, label: 'Who to Send It To', icon: 'account_box' }, { num: 3, label: 'What Happened', icon: 'fact_check' }, { num: 4, label: 'Official Letter', icon: 'edit_document' }, { num: 5, label: 'Check Weak Points', icon: 'health_and_safety' }, { num: 6, label: 'Translate Official Reply', icon: 'translate' }, ]; return (
{/* Top Breadcrumb & Status Header */}
/ / {currentCase.caseNumber}

{currentCase.title}

{currentCase.category}
account_balance Law: {currentCase.statute} • location_on {currentCase.officer?.jurisdiction || 'Local Jurisdiction'} • timer {currentCase.daysRemaining || 30} Days to Reply
{/* Action Controls */}
health_and_safety Strength: {currentCase.score || 88}%
{/* Step Tabs Navigation Bar */}
{STEPS.map((step) => { const isActive = activeStep === step.num; return ( ); })}
{/* STEP 1: HIDE PRIVATE INFO */} {activeStep === 1 && (

shield Hide Private Information

Automatically masks your phone number, bank details, and personal address to keep your filing secure.

{(currentCase.piiItems || []).filter((p) => p.masked).length} of {(currentCase.piiItems || []).length} Hidden
Private details detected:
{(currentCase.piiItems || []).length === 0 ? (
No sensitive financial or personal identity details detected in this filing.
) : ( (currentCase.piiItems || []).map((item) => (
{item.type}

{item.original}

)) )}
Protected Text Preview:

{currentCase.redactedText || currentCase.rawIntakeText || currentCase.description}

info Protected tokens (e.g. [REDACTED_REQUESTER]) ensure your private data is shielded when notices are served.
)} {/* STEP 2: WHO TO SEND IT TO */} {activeStep === 2 && (

account_box Who to Send It To

Real, designated public authority, grievance nodal officer, or corporate legal cell in {currentCase.officer?.jurisdiction || 'your city'}.

{/* Primary Officer Card */}
{currentCase.officer?.avatar || 'PO'}

{currentCase.officer?.name || 'Public Information Officer'}

{currentCase.officer?.title || 'Designated Nodal Officer'}

Department: {currentCase.officer?.department || 'Civic Grievance Redressal Division'}
Jurisdiction / Area: {currentCase.officer?.jurisdiction || 'Local Office'}
Official Email: {currentCase.officer?.email || 'records.intake@civic-gateway.gov'}
{currentCase.officer?.address && (
Office Address: {currentCase.officer.address}
)} {currentCase.officer?.portalUrl && ( )}
verified Designated officer & legal timeline ({currentCase.daysRemaining || 30} days) active for this statutory filing.
{/* Live Indian Authority Directory Search */}
search Search Real Department Directory

Look up real public authorities, consumer forums, police nodal cells, or municipal offices in {currentCase.officer?.jurisdiction || 'your city'}.

setSearchQuery(e.target.value)} placeholder="e.g. BBMP Solid Waste, Consumer Forum, BESCOM, Police PIO" className="flex-1 bg-white border border-[#CBD5E1] rounded-xl px-3 py-2 text-[13px] text-[#0F172A] outline-none focus:border-[#006c4a]" />
{searchError && (
{searchError}
)} {searchResult && (
{searchResult.department} Verified

{searchResult.officerTitle}

Email: {searchResult.nodalEmail}

Address: {searchResult.officeAddress}

{searchResult.grievancePortal && (

Portal: {searchResult.grievancePortal}

)}
)}
)} {/* STEP 3: WHAT HAPPENED */} {activeStep === 3 && (

fact_check What Happened (Chronological Facts)

Gemini synthesized your narrative into legally structured, chronological facts. You can edit or add points.

{/* Add Fact Form */} {showAddFact && (
setNewFactLabel(e.target.value)} placeholder="e.g. Transaction Ref / Incident Date / Disputed Amount" className="w-full bg-white border border-[#CBD5E1] rounded-lg px-3 py-2 text-[13px] text-[#0F172A] outline-none focus:border-[#006c4a]" required />
setNewFactValue(e.target.value)} placeholder="e.g. ₹1,40,000 security deposit withheld despite clean exit handover on Jan 15" className="w-full bg-white border border-[#CBD5E1] rounded-lg px-3 py-2 text-[13px] text-[#0F172A] outline-none focus:border-[#006c4a]" required />
)}
{(currentCase.facts || []).map((fact) => (
{fact.label}

{fact.value}

))}
Specific Relief / Remedy Demanded: