RedactX / src /ui /App.tsx
GitHub Actions
Deploy from GitHub
ac4a6d6
Raw
History Blame Contribute Delete
11.6 kB
import React, { useState, Suspense } from 'react';
import { HashRouter, Routes, Route, useNavigate, Navigate } from 'react-router-dom';
import {
FileText, FileUp, Clock, LogOut, Loader2, CheckCircle2,
Shield, Activity, Lock, Terminal
} from 'lucide-react';
import { Header } from './components/Header';
import { Sidebar } from './components/Sidebar';
import { RedactionCard } from './components/RedactionCard';
import { useThemeStore } from './store/theme';
// Lazy loading components for code splitting & bundle reduction
const TextRedaction = React.lazy(() => import('./pages/TextRedaction').then(m => ({ default: m.TextRedaction })));
const PDFRedaction = React.lazy(() => import('./pages/PDFRedaction').then(m => ({ default: m.PDFRedaction })));
const History = React.lazy(() => import('./pages/History').then(m => ({ default: m.History })));
const AccountDetails = React.lazy(() => import('./pages/AccountDetails').then(m => ({ default: m.AccountDetails })));
const HowToUse = React.lazy(() => import('./pages/HowToUse').then(m => ({ default: m.HowToUse })));
const AboutUs = React.lazy(() => import('./pages/AboutUs').then(m => ({ default: m.AboutUs })));
const ContactUs = React.lazy(() => import('./pages/ContactUs').then(m => ({ default: m.ContactUs })));
const RegisterPage = React.lazy(() => import('./pages/Register'));
const LoginPage = React.lazy(() => import('./pages/Login'));
const LandingPage = React.lazy(() => import('./pages/Landing').then(m => ({ default: m.Landing })));
function PageLoader() {
return (
<div className="min-h-[70vh] flex flex-col items-center justify-center text-slate-400 font-sans">
<Loader2 className="w-8 h-8 animate-spin text-red-500 mb-3" />
<p className="text-sm font-mono tracking-wider uppercase">Loading Module...</p>
</div>
);
}
function ProtectedRoute({ children }: { children: React.ReactElement }) {
const token = localStorage.getItem('token');
if (!token) {
return <Navigate to="/landing" replace />;
}
return children;
}
function Home() {
const navigate = useNavigate();
const redactionOptions = [
{
icon: FileText,
title: 'Text Redaction',
description: 'Obfuscate PII, Indian numbers, names, and custom confidential text in real-time.',
path: '/text-redaction',
},
{
icon: FileUp,
title: 'File Redaction',
description: 'Sanitize PDF documents, Word files, Excel spreadsheets, and scanned images offline.',
path: '/pdf-redaction',
},
{
icon: Clock,
title: 'History',
description: 'Review your recent redaction logs and processed files stored in local memory.',
path: '/history',
},
];
const handleLogout = () => {
localStorage.removeItem('token');
window.location.href = '/#/login';
};
return (
<main className="pt-24 px-6 pb-16 max-w-6xl mx-auto font-sans relative overflow-hidden">
{/* Dynamic Ambient Background Animations */}
<div className="absolute top-10 left-1/2 -translate-x-1/2 w-[800px] h-[300px] bg-gradient-to-r from-red-600/15 via-red-900/10 to-blue-600/10 blur-3xl pointer-events-none -z-10 animate-pulse" />
<div className="absolute top-1/3 right-10 w-72 h-72 bg-red-500/10 rounded-full blur-2xl pointer-events-none -z-10 animate-bounce duration-1000" />
<div className="absolute inset-0 bg-[linear-gradient(to_right,#1e293b10_1px,transparent_1px),linear-gradient(to_bottom,#1e293b10_1px,transparent_1px)] bg-[size:3rem_3rem] pointer-events-none -z-20" />
{/* Hero Welcome Banner */}
<div className="bg-slate-900/80 border border-slate-800 backdrop-blur-md p-8 rounded-sm mb-10 shadow-2xl relative overflow-hidden group hover:border-slate-700 transition-all">
<div className="absolute -right-10 -bottom-10 w-48 h-48 bg-red-600/10 rounded-full blur-2xl group-hover:bg-red-600/20 transition-all pointer-events-none" />
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-6 relative z-10">
<div className="space-y-3">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-sm bg-red-950/60 border border-red-500/30 text-red-400 text-xs font-mono font-bold tracking-wider uppercase">
<Shield className="w-3.5 h-3.5 text-red-500 animate-pulse" />
<span>Security Console Active</span>
</div>
<h1 className="text-3xl md:text-4xl font-extrabold text-white tracking-tight font-sans">
Welcome to <span className="text-red-500 underline decoration-red-600/50 underline-offset-4">RE-DACT</span>
</h1>
<p className="text-sm text-slate-400 max-w-2xl leading-relaxed">
Your localized workspace for stripping sensitive personal identifiers, Indian regulatory numbers, and financial data using advanced offline NLP and synthetic anonymization.
</p>
</div>
<button
onClick={handleLogout}
className="flex items-center gap-2 px-5 py-2.5 text-xs font-mono uppercase font-bold text-red-400 bg-slate-950 hover:bg-red-950/40 border border-slate-800 hover:border-red-900/60 rounded-sm transition-all shrink-0 shadow-lg"
>
<LogOut className="w-4 h-4" />
<span>Logout Account</span>
</button>
</div>
{/* Live Status Indicators */}
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 mt-8 pt-6 border-t border-slate-800/80 font-mono text-xs">
<div className="flex items-center gap-2.5 text-slate-300">
<span className="w-2 h-2 rounded-full bg-emerald-500 animate-ping" />
<span className="font-bold">100% Offline </span>
</div>
<div className="flex items-center gap-2.5 text-slate-300">
<CheckCircle2 className="w-4 h-4 text-blue-400 shrink-0" />
<span>Trained and Finetuned Models</span>
</div>
<div className="flex items-center gap-2.5 text-slate-300">
<Lock className="w-4 h-4 text-red-400 shrink-0" />
<span>Zero Data Retention</span>
</div>
<div className="flex items-center gap-2.5 text-slate-300">
<Activity className="w-4 h-4 text-emerald-400 shrink-0" />
<span>5 Redaction Levels</span>
</div>
</div>
</div>
{/* Module Selection Section */}
<div className="space-y-4">
<div className="flex items-center justify-between border-b border-slate-800/80 pb-3">
<h2 className="text-lg font-bold text-white tracking-wide uppercase font-mono flex items-center gap-2">
<Terminal className="w-4 h-4 text-red-500" />
Select Sanitization Module
</h2>
<span className="text-xs text-slate-500 font-mono">Choose a module to launch</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 pt-2">
{redactionOptions.map((option) => (
<RedactionCard
key={option.title}
icon={option.icon}
title={option.title}
description={option.description}
onClick={() => navigate(option.path)}
/>
))}
</div>
</div>
</main>
);
}
function App() {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const { theme } = useThemeStore();
return (
<HashRouter>
<div className={theme}>
<div className="min-h-screen bg-slate-950 text-slate-100 selection:bg-red-600 selection:text-white font-sans">
<Suspense fallback={<PageLoader />}>
<Routes>
<Route
path="/home"
element={
<ProtectedRoute>
<>
<Header onMenuClick={() => setIsSidebarOpen(true)} />
<Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} />
<Home />
</>
</ProtectedRoute>
}
/>
<Route path="/" element={<Navigate to="/home" replace />} />
<Route
path="/text-redaction"
element={
<ProtectedRoute>
<>
<Header onMenuClick={() => setIsSidebarOpen(true)} />
<Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} />
<TextRedaction />
</>
</ProtectedRoute>
}
/>
<Route
path="/pdf-redaction"
element={
<ProtectedRoute>
<>
<Header onMenuClick={() => setIsSidebarOpen(true)} />
<Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} />
<PDFRedaction />
</>
</ProtectedRoute>
}
/>
<Route
path="/history"
element={
<ProtectedRoute>
<>
<Header onMenuClick={() => setIsSidebarOpen(true)} />
<Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} />
<History />
</>
</ProtectedRoute>
}
/>
<Route
path="/account-details"
element={
<ProtectedRoute>
<>
<Header onMenuClick={() => setIsSidebarOpen(true)} />
<Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} />
<AccountDetails />
</>
</ProtectedRoute>
}
/>
<Route
path="/how-to-use"
element={
<ProtectedRoute>
<>
<Header onMenuClick={() => setIsSidebarOpen(true)} />
<Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} />
<HowToUse />
</>
</ProtectedRoute>
}
/>
<Route
path="/about-us"
element={
<ProtectedRoute>
<>
<Header onMenuClick={() => setIsSidebarOpen(true)} />
<Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} />
<AboutUs />
</>
</ProtectedRoute>
}
/>
<Route
path="/contact-us"
element={
<ProtectedRoute>
<>
<Header onMenuClick={() => setIsSidebarOpen(true)} />
<Sidebar isOpen={isSidebarOpen} onClose={() => setIsSidebarOpen(false)} />
<ContactUs />
</>
</ProtectedRoute>
}
/>
<Route path="/landing" element={<LandingPage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
</Routes>
</Suspense>
</div>
</div>
</HashRouter>
);
}
export default App;