import { useState, useEffect } from 'react';
export default function App() {
// Auth0 Configuration - Your actual Auth0 credentials
const AUTH0_DOMAIN = 'ahnaflegaldatabase.au.auth0.com';
const AUTH0_CLIENT_ID = 'UXoGRxPU4l2tZdNyECGOMLnJendWpPsn';
const AUTH0_REDIRECT_URI = window.location.origin;
// Authentication state
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [currentUser, setCurrentUser] = useState(null);
const [showLogin, setShowLogin] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [authError, setAuthError] = useState('');
// Auth0 token and user management
const [accessToken, setAccessToken] = useState(null);
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [activeTab, setActiveTab] = useState('dashboard');
const [showAddForm, setShowAddForm] = useState(false);
const [notification, setNotification] = useState({ message: '', type: '' });
const [selectedDoc, setSelectedDoc] = useState(null);
const [filters, setFilters] = useState({
type: '',
jurisdiction: '',
dateFrom: '',
dateTo: ''
});
const [showFilters, setShowFilters] = useState(false);
const [searchHistory, setSearchHistory] = useState([]);
// Load documents from localStorage or use default data
const loadDocumentsFromStorage = () => {
try {
const stored = localStorage.getItem('aharenge_documents');
if (stored) {
return JSON.parse(stored);
}
} catch (error) {
console.error('Error loading documents from storage:', error);
}
// Return default sample data if nothing stored or error occurred
return [
{
id: 1,
type: 'case_law',
title: 'Smith v. Jones',
dhivehi_title: 'ސްމިތް ވީ. ޖޯންސް',
jurisdiction: 'California',
date: '2022-03-15',
citation: '123 Cal. App. 4th 567 (2022)',
keywords: ['contract', 'employment', 'breach'],
judges: ['Justice Williams', 'Justice Chen', 'Justice Rodriguez'],
introduction: {
en: 'This case involves a breach of contract dispute between an employer and employee regarding non-compete clauses and severance agreements.',
dv: 'މި ކޭސް އަކީ މުވައްޒަފާއި މުވައްޒަފުގެ ދަށުން ކޮންޓްރެކްޓް ބްރީޗް ދެކޮޅަށް ވާ ކޭސެކެވެ.'
},
ratio: {
en: 'The court held that failure to fulfill contractual obligations constitutes breach when material terms are violated without justification.',
dv: 'ކޯޓުން ނިންމީ ކޮންޓްރެކްޓް އޮބްލިގޭޝަންތައް ފުރިހަމަ ނުކުރުމަކީ ބްރީޗް ކަމުގައެވެ.'
},
abstracts: [
{
en: 'In the matter of Smith v. Jones, the court ruled on the enforceability of non-compete clauses in employment contracts.',
dv: 'ސްމިތް ވީ. ޖޯންސް ކޭސްގައި ކޯޓުން ނިންމީ ނޮން-ކޮމްޕީޓް ކްލޯސްތަކުގެ ބާރަށް ޢަމަލުކުރެވޭ ކަމެވެ.',
page: 'p. 567',
paragraph: '¶ 12'
}
]
},
{
id: 2,
type: 'statute',
title: 'Consumer Protection Act',
dhivehi_title: 'ސަރުކާރުގެ ޙިމާޔަތުގެ ޤާނޫނު',
jurisdiction: 'Federal',
date: '2023-01-20',
citation: '15 U.S.C. § 1681',
keywords: ['consumer', 'protection', 'fraud', 'warranty'],
body: {
en: 'This statute provides comprehensive consumer protection measures against fraudulent business practices and defective products. It establishes clear guidelines for warranty claims, refund policies, and dispute resolution mechanisms.',
dv: 'މި ޤާނޫނުން ފުރިހަމަ ކަސްޓަމަރުގެ ޙިމާޔަތް ފޯރުކޮށްދޭ ފުރޯޑް އަދި ކްރޮޅު ތަކެތީގެ ކުރިއެރުން. މި ޤާނޫނުން ވޮރަންޓީ ކްލެއިމް، ރިފަންޑް ޕޮލިސީ އަދި ޑިސްޕިއުޓް ރިޒޮލިއުޝަން މެކެނިޒަމަށް ސާފު ގައިޑްލައިން ބަޔާންކޮށްދެއެވެ.'
}
},
{
id: 3,
type: 'legal_article',
title: 'Recent Developments in Contract Law',
dhivehi_title: 'ކޮންޓްރެކްޓް ލޯގެ އެއްވެރި ކުރިމަތިއްޔާއިގެ އެއްވެރި ކުރިމަތިއްޔާ',
jurisdiction: 'International',
date: '2024-05-15',
citation: 'Int\'l L.J. 2024 Vol. 1',
keywords: ['contract', 'law', 'development', 'analysis'],
body: {
en: 'This article analyzes recent developments in contract law across multiple jurisdictions, focusing on digital contracts, force majeure clauses, and evolving standards of good faith. The analysis covers landmark cases, statutory reforms, and emerging legal principles that shape modern contractual relationships.',
dv: 'މި އަރިކަލް ކޮންޓްރެކްޓް ލޯގެ އެއްވެރި ކުރިމަތިއްޔާ ބާރުކުރެވޭ އެވެ، ޑިޖިޓަލް ކޮންޓްރެކްޓް، ފޮސް މާޖުރް ކްލޯސް، އަދި ގޫމް ފޭތް ސްޓެންޑާރޑްތަކުގެ އެއްވެރި ކުރިމަތިއްޔާ ރިވިއު ކުރެއެވެ. މި ބާރުކުރުމުގައި ލޭންޑްމާކް ކޭސްތައް، ޤާނޫނީ ރިފޯމް، އަދި މޮޑަން ކޮންޓްރެކްޗުއަލް ރިލޭޝަންޝިޕްތައް ޝޭޕްކުރާ އިމަރޖިންގ ލީގަލް ޕްރިންސިޕަލްތައް ހިމެނެއެވެ.'
}
},
{
id: 4,
type: 'case_law',
title: 'Johnson v. Pacific Corp',
dhivehi_title: 'ޖޮންސަން ވީ. ޕެސިފިކް ކޯޕް',
jurisdiction: 'New York',
date: '2023-11-08',
citation: '2023 NY Slip Op 05234',
keywords: ['corporate', 'liability', 'negligence', 'damages'],
judges: ['Judge Thompson', 'Judge Martinez'],
introduction: {
en: 'A landmark case establishing corporate liability standards for negligent supervision in workplace accidents.',
dv: 'މަސައްކަތުގެ ތަނުގެ ހާދިސާތަކުގައި ނެގްލިޖަންޓް ސުޕަވިޝަންއަށް ކޯޕޮރޭޓް ލަޔަބިލިޓީ ސްޓެންޑަރޑް ޤާއިމްކުރި މުހިންމު ކޭސެކެވެ.'
},
ratio: {
en: 'Corporations have a non-delegable duty to ensure adequate supervision of safety protocols.',
dv: 'ކޯޕޮރޭޝަންތަކުން ސޭފްޓީ ޕްރޮޓޮކޯލްތަކުގެ ފުރިހަމަ ސުޕަވިޝަން ކަށަވަރު ކުރުމުގެ މަސްއޫލިއްޔަތެއް އެވެ.'
},
abstracts: [
{
en: 'The court found that Pacific Corp failed to implement adequate safety measures despite prior warnings.',
dv: 'ކޯޓުން ފެނުނީ ޕެސިފިކް ކޯޕްއަށް ކުރިން ވާރުނިންގް ދީފިނަމަވެސް ފުރިހަމަ ސޭފްޓީ މިންހާސް ޢަމަލުކުރެއްނުވި ކަމެވެ.',
page: 'p. 234',
paragraph: '¶ 45'
}
]
},
{
id: 5,
type: 'statute',
title: 'Data Privacy Protection Act',
dhivehi_title: 'ޑޭޓާ ޕްރައިވަސީ ޙިމާޔަތުގެ ޤާނޫނު',
jurisdiction: 'California',
date: '2024-01-01',
citation: 'Cal. Civ. Code § 1798.100',
keywords: ['privacy', 'data', 'protection', 'rights'],
body: {
en: 'This act establishes comprehensive data privacy rights for California residents, including the right to know, delete, and opt-out of the sale of personal information. Companies must provide clear disclosures about data collection practices and implement reasonable security measures.',
dv: 'މި ޤާނޫނުން ކެލިފޯނިޔާ ރެސިޑެންޓްތަކަށް ފުރިހަމަ ޑޭޓާ ޕްރައިވަސީ ޙައްޤުތައް ޤާއިމްކުރޭ، ނޭޖަހާ، ޑިލީޓް، އަދި ޕާސަނަލް އިންފޮމޭޝަން ވިއްކުމުން އާއްޗް އައުޓް ވުމުގެ ޙައްޤު ހިމެނޭ. ކުންފުނިތަކުން ޑޭޓާ ސަގުކުރުމުގެ ޕްރެކްޓިސްތަކާ މެދު ސާފު ޑިސްކްލޯޝަރ ދޭންވާނެ އަދި އަކުރަ ސެކިއުރިޓީ މިންހާސް ތައް ޢަމަލުކުރަންވާނެއެވެ.'
}
},
{
id: 6,
type: 'legal_article',
title: 'AI and Legal Ethics: Emerging Challenges',
dhivehi_title: 'އޭއައި އަދި ލީގަލް އެތިކްސް: އުފެދޭ ގޮންޖެހުން',
jurisdiction: 'International',
date: '2024-06-20',
citation: 'Tech Law Review 2024 Vol. 15',
keywords: ['artificial intelligence', 'ethics', 'legal profession', 'technology'],
body: {
en: 'This comprehensive analysis explores the ethical implications of AI adoption in legal practice, addressing concerns about bias, accountability, and professional responsibility. The article examines case studies, regulatory frameworks, and best practices for ethical AI implementation in law firms.',
dv: 'މި ފުރިހަމަ ތަޙްލީލުން ލީގަލް ޕްރެކްޓިސްގައި އޭއައި ބޭނުންކުރުމުގެ އެތިކަލް އަސަރުތައް ބަލާ، ބަޔާސް، އެކައުންޓެބިލިޓީ، އަދި ޕްރޮފެޝަނަލް ރެސްޕޮންސިބިލިޓީގެ ކަންބޮޑުވުންތައް ކުރިއަށް ގެންދޭ. މި އަރޓިކަލްގައި ކޭސް ސްޓަޑީ، ރެގިއުލޭޓަރީ ފްރޭމްވާކް، އަދި ލޯ ފާމްތަކުގައި އެތިކަލް އޭއައި އިމްޕްލިމަންޓޭޝަނަށް ރަނގަޅު ގޮތްތައް ބައްލަވާނެއެވެ.'
}
}
];
};
const [legalDocuments, setLegalDocuments] = useState(() => loadDocumentsFromStorage());
// Save documents to localStorage whenever legalDocuments changes
useEffect(() => {
try {
localStorage.setItem('aharenge_documents', JSON.stringify(legalDocuments));
console.log('Documents saved to localStorage');
} catch (error) {
console.error('Error saving documents to storage:', error);
showNotification('Warning: Documents could not be saved locally', 'warning');
}
}, [legalDocuments]);
// Initialize results with all documents on first load
useEffect(() => {
if (isAuthenticated) {
setResults(legalDocuments);
}
}, [isAuthenticated, legalDocuments]);
// Calculate next ID safely
const nextId = legalDocuments.reduce((max, doc) => Math.max(max, doc.id), 0) + 1;
// Enhanced new document state
const [newDoc, setNewDoc] = useState({
type: 'case_law',
title: '',
dhivehi_title: '',
jurisdiction: '',
date: '',
citation: '',
keywords: '',
judges: '',
introduction: { en: '', dv: '' },
ratio: { en: '', dv: '' },
body: { en: '', dv: '' },
abstracts: [{ en: '', dv: '', page: '', paragraph: '' }]
});
// Auth0 Integration Functions
// Initialize Auth0 on component mount
useEffect(() => {
initializeAuth0();
}, []);
const initializeAuth0 = async () => {
try {
setIsLoading(true);
// Check for existing token in localStorage
const storedToken = localStorage.getItem('auth0_access_token');
const storedUser = localStorage.getItem('auth0_user');
if (storedToken && storedUser) {
// Verify token is still valid (simplified check)
try {
const userInfo = JSON.parse(storedUser);
setAccessToken(storedToken);
setCurrentUser({
id: userInfo.sub,
name: userInfo.name || userInfo.email,
email: userInfo.email,
role: userInfo['https://aharenge.com/roles']?.[0] || 'user', // Custom claim for role
picture: userInfo.picture
});
setIsAuthenticated(true);
showNotification(`Welcome back, ${userInfo.name || userInfo.email}!`, 'success');
} catch (e) {
// Clear invalid stored data
localStorage.removeItem('auth0_access_token');
localStorage.removeItem('auth0_user');
}
}
// Check for Auth0 callback (code in URL)
const urlParams = new URLSearchParams(window.location.search);
const authCode = urlParams.get('code');
const state = urlParams.get('state');
const storedState = localStorage.getItem('auth0_state');
if (authCode) {
// Verify the state parameter to prevent CSRF attacks
if (state && state === storedState) {
await handleAuthCallback(authCode);
} else {
console.error('State mismatch - possible CSRF attack');
setAuthError('Authentication failed - security validation error');
showNotification('Authentication failed - security error', 'error');
}
}
} catch (error) {
console.error('Auth0 initialization error:', error);
setAuthError('Failed to initialize authentication');
} finally {
setIsLoading(false);
}
};
const handleAuthCallback = async (code) => {
try {
showNotification('Processing login...', 'info');
// Exchange code for tokens using Auth0 token endpoint
const verifier = localStorage.getItem('auth0_code_verifier');
if (!verifier) {
throw new Error('Code verifier not found');
}
const tokenResponse = await fetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
grant_type: 'authorization_code',
client_id: AUTH0_CLIENT_ID,
code_verifier: verifier,
code: code,
redirect_uri: AUTH0_REDIRECT_URI
})
}).catch(error => {
console.error('Network error connecting to Auth0:', error);
throw new Error('Cannot connect to Auth0 server. Please check your Auth0 domain configuration.');
});
if (!tokenResponse.ok) {
throw new Error('Failed to exchange code for tokens');
}
const tokens = await tokenResponse.json();
// Get user information using the access token
const userInfoResponse = await fetch(`https://${AUTH0_DOMAIN}/userinfo`, {
headers: {
'Authorization': `Bearer ${tokens.access_token}`
}
});
if (!userInfoResponse.ok) {
throw new Error('Failed to fetch user information');
}
const userInfo = await userInfoResponse.json();
// Save tokens and user info to localStorage
localStorage.setItem('auth0_access_token', tokens.access_token);
localStorage.setItem('auth0_id_token', tokens.id_token);
localStorage.setItem('auth0_user', JSON.stringify(userInfo));
// Set user in application state
setAccessToken(tokens.access_token);
setCurrentUser({
id: userInfo.sub,
name: userInfo.name || userInfo.email,
email: userInfo.email,
role: userInfo['https://aharenge.com/roles']?.[0] || 'user', // Custom claim for role
picture: userInfo.picture
});
setIsAuthenticated(true);
// Clear the URL parameters
window.history.replaceState({}, document.title, window.location.pathname);
showNotification(`Welcome, ${userInfo.name || userInfo.email}!`, 'success');
} catch (error) {
console.error('Auth callback error:', error);
setAuthError('Failed to complete login');
showNotification('Authentication failed. Please try again.', 'error');
}
};
// Generate a code verifier for PKCE (Proof Key for Code Exchange)
const generateCodeVerifier = () => {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return btoa(String.fromCharCode.apply(null, array))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
};
// Generate a code challenge from the code verifier
const generateCodeChallenge = async (codeVerifier) => {
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const digest = await window.crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode.apply(null, new Uint8Array(digest)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
};
const loginWithAuth0 = async () => {
try {
// First, test if we can reach the Auth0 domain
const testUrl = `https://${AUTH0_DOMAIN}/.well-known/openid_configuration`;
try {
const testResponse = await fetch(testUrl, { method: 'HEAD' });
if (!testResponse.ok) {
throw new Error('Auth0 domain not reachable');
}
} catch (testError) {
console.error('Auth0 domain test failed:', testError);
showNotification('Cannot connect to Auth0. Please check your domain configuration or use Demo Login instead.', 'error');
return;
}
// Generate PKCE code verifier and challenge
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
// Store code verifier in localStorage for later use
localStorage.setItem('auth0_code_verifier', codeVerifier);
// Generate state parameter to prevent CSRF attacks
const state = generateRandomState();
localStorage.setItem('auth0_state', state);
// Build Auth0 authorization URL with PKCE
const authUrl = `https://${AUTH0_DOMAIN}/authorize?` +
`response_type=code&` +
`client_id=${AUTH0_CLIENT_ID}&` +
`redirect_uri=${encodeURIComponent(AUTH0_REDIRECT_URI)}&` +
`scope=openid profile email&` +
`state=${state}&` +
`code_challenge=${codeChallenge}&` +
`code_challenge_method=S256`;
// Redirect to Auth0 login
window.location.href = authUrl;
} catch (error) {
console.error('Error initiating Auth0 login:', error);
showNotification('Auth0 connection failed. Please use Demo Login or check your Auth0 configuration.', 'error');
}
};
const generateRandomState = () => {
return Math.random().toString(36).substring(2) + Date.now().toString(36);
};
const handleLogout = () => {
// Clear all Auth0-related items from local storage
localStorage.removeItem('auth0_access_token');
localStorage.removeItem('auth0_id_token');
localStorage.removeItem('auth0_user');
localStorage.removeItem('auth0_state');
localStorage.removeItem('auth0_code_verifier');
// Reset application state
setIsAuthenticated(false);
setCurrentUser(null);
setAccessToken(null);
setActiveTab('dashboard');
setResults([]);
// Build Auth0 logout URL
const logoutUrl = `https://${AUTH0_DOMAIN}/v2/logout?` +
`client_id=${AUTH0_CLIENT_ID}&` +
`returnTo=${encodeURIComponent(window.location.origin)}`;
showNotification('Logged out successfully', 'info');
// Redirect to Auth0 logout endpoint
window.location.href = logoutUrl;
};
// Alternative: Simple demo login (fallback if Auth0 not configured)
const demoLogin = async () => {
try {
setIsLoading(true);
// Simulate API call delay
await new Promise(resolve => setTimeout(resolve, 1000));
// Create demo user
const demoUser = {
id: 'demo_' + Date.now(),
name: 'Demo User',
email: 'demo@aharenge.com',
role: 'admin', // Give demo user admin privileges
picture: null
};
setCurrentUser(demoUser);
setIsAuthenticated(true);
setShowLogin(false);
setActiveTab('dashboard');
// Store demo session
localStorage.setItem('demo_user', JSON.stringify(demoUser));
showNotification(`Welcome, ${demoUser.name}!`, 'success');
} catch (error) {
setAuthError('Demo login failed');
} finally {
setIsLoading(false);
}
};
// Custom SVG Icons
const SearchIcon = () => (
);
const BookOpenIcon = () => (
);
const DashboardIcon = () => (
);
const PlusIcon = () => (
);
const XIcon = () => (
);
const FileTextIcon = () => (
);
const CalendarIcon = () => (
);
const MapPinIcon = () => (
);
const TagIcon = () => (
);
const EyeIcon = () => (
);
const TrashIcon = () => (
);
const FilterIcon = () => (
);
const DownloadIcon = () => (
);
const DocumentIcon = () => (
);
const UserIcon = () => (
);
const LogoutIcon = () => (
);
const LoginIcon = () => (
);
// Auto-hide notifications
useEffect(() => {
if (notification.message) {
const timer = setTimeout(() => {
setNotification({ message: '', type: '' });
}, 5000);
return () => clearTimeout(timer);
}
}, [notification]);
const showNotification = (message, type = 'success') => {
setNotification({ message, type });
};
// CSV Export function
const exportToCSV = () => {
const headers = ['ID', 'Type', 'Title', 'Jurisdiction', 'Date', 'Citation', 'Keywords', 'Judges'];
const csvData = legalDocuments.map(doc => [
doc.id,
doc.type,
doc.title || doc.dhivehi_title,
doc.jurisdiction,
doc.date,
doc.citation || 'N/A',
doc.keywords.join('; '),
doc.judges ? doc.judges.join('; ') : 'N/A'
]);
const csvContent = [headers, ...csvData]
.map(row => row.map(field => `"${field}"`).join(','))
.join('\n');
const blob = new Blob([csvContent], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'aharenge_documents.csv';
link.click();
window.URL.revokeObjectURL(url);
showNotification('CSV export completed', 'success');
};
// PDF Export function - simplified approach
const exportToPDF = () => {
try {
// Simple text-based PDF export that works reliably
const pdfContent = legalDocuments.map((doc, index) => {
let content = `${index + 1}. ${doc.title || doc.dhivehi_title}\n`;
content += `Jurisdiction: ${doc.jurisdiction}\n`;
content += `Date: ${doc.date}\n`;
if (doc.citation) content += `Citation: ${doc.citation}\n`;
if (doc.keywords?.length) content += `Keywords: ${doc.keywords.join(', ')}\n`;
if (doc.judges?.length) content += `Judges: ${doc.judges.join(', ')}\n`;
if (doc.type === 'case_law') {
if (doc.introduction?.en) content += `\nIntroduction:\n${doc.introduction.en}\n`;
if (doc.ratio?.en) content += `\nRatio Decidendi:\n${doc.ratio.en}\n`;
} else if (doc.body?.en) {
content += `\nContent:\n${doc.body.en}\n`;
}
return content + '\n' + '='.repeat(80) + '\n\n';
}).join('');
const fullContent = `AHARENGE DATABASE - DOCUMENT EXPORT
Generated: ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}
${'='.repeat(80)}
${pdfContent}`;
// Create and download as PDF-like text file
const blob = new Blob([fullContent], { type: 'application/pdf' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `aharenge_documents_${new Date().toISOString().split('T')[0]}.pdf`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
showNotification('Document export completed successfully', 'success');
} catch (error) {
console.error('Export error:', error);
showNotification('Export failed. Please try again.', 'error');
}
};
const parseQuery = (input) => {
// Enhanced parsing to handle both English and Dhivehi text
const tokens = input.match(/"[^"]+"|\S+/g) || [];
const parsed = { keywords: [], jurisdictions: [] };
const jurisdictionRegex = /\b(california|new york|ny|federal|maldives|mv|ކެލިފޯނިޔާ|ނިއު ޔޯކް|ފެޑެރަލް|ދިވެހިރާއްޖެ)\b/gi;
let match;
while ((match = jurisdictionRegex.exec(input)) !== null) {
parsed.jurisdictions.push(match[0].toLowerCase());
}
tokens.forEach(token => {
if (token.startsWith('"') && token.endsWith('"')) {
// Quoted phrase - keep as is (remove quotes)
parsed.keywords.push(token.slice(1, -1));
} else {
// Individual words - split on whitespace but preserve Dhivehi characters
const words = token.split(/\s+/).filter(word => word.trim().length > 0);
parsed.keywords.push(...words);
}
});
// Remove empty keywords and duplicates
parsed.keywords = [...new Set(parsed.keywords.filter(keyword => keyword.trim().length > 0))];
return parsed;
};
// Database Backup and Restore Functions
const exportDatabase = () => {
try {
const databaseExport = {
version: '1.0',
exportDate: new Date().toISOString(),
documentCount: legalDocuments.length,
documents: legalDocuments
};
const dataStr = JSON.stringify(databaseExport, null, 2);
const blob = new Blob([dataStr], { type: 'application/json' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `aharenge_database_backup_${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
showNotification(`Database backup completed! ${legalDocuments.length} documents exported.`, 'success');
} catch (error) {
console.error('Export error:', error);
showNotification('Failed to export database. Please try again.', 'error');
}
};
const importDatabase = (event) => {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
try {
const importedData = JSON.parse(e.target.result);
// Validate the imported data
if (!importedData.documents || !Array.isArray(importedData.documents)) {
throw new Error('Invalid file format');
}
// Ask user for confirmation
const confirmMessage = `This will replace your current database with ${importedData.documents.length} documents from the backup file. Your current ${legalDocuments.length} documents will be lost. Are you sure?`;
if (window.confirm(confirmMessage)) {
setLegalDocuments(importedData.documents);
setResults(importedData.documents);
showNotification(`Database restored successfully! ${importedData.documents.length} documents imported.`, 'success');
}
} catch (error) {
console.error('Import error:', error);
showNotification('Failed to import database. Please check the file format.', 'error');
}
};
reader.readAsText(file);
// Reset the input so the same file can be selected again
event.target.value = '';
};
// Enhanced full-text search function
const searchInText = (text, searchTerms) => {
if (!text || !searchTerms.length) return false;
const normalizedText = text.toLowerCase().trim();
return searchTerms.some(term => normalizedText.includes(term.toLowerCase()));
};
const search = () => {
if (!query.trim()) {
showNotification('Please enter a search query', 'error');
return;
}
const { keywords, jurisdictions } = parseQuery(query);
const matchedDocuments = [];
// Enhanced search that looks through all text content
legalDocuments.forEach(doc => {
let hasMatch = false;
// Search in basic fields
if (searchInText(doc.title, keywords) ||
searchInText(doc.dhivehi_title, keywords) ||
searchInText(doc.citation, keywords) ||
searchInText(doc.jurisdiction, keywords)) {
hasMatch = true;
}
// Search in keywords array
if (!hasMatch && doc.keywords.some(keyword =>
keywords.some(term => keyword.toLowerCase().includes(term.toLowerCase())))) {
hasMatch = true;
}
// Search in judges array (for case law)
if (!hasMatch && doc.judges && doc.judges.some(judge =>
keywords.some(term => judge.toLowerCase().includes(term.toLowerCase())))) {
hasMatch = true;
}
// Search in case law specific fields
if (!hasMatch && doc.type === 'case_law') {
// Search in introduction
if (doc.introduction) {
if (searchInText(doc.introduction.en, keywords) ||
searchInText(doc.introduction.dv, keywords)) {
hasMatch = true;
}
}
// Search in ratio decidendi
if (!hasMatch && doc.ratio) {
if (searchInText(doc.ratio.en, keywords) ||
searchInText(doc.ratio.dv, keywords)) {
hasMatch = true;
}
}
// Search in abstracts
if (!hasMatch && doc.abstracts) {
for (const abstract of doc.abstracts) {
if (searchInText(abstract.en, keywords) ||
searchInText(abstract.dv, keywords) ||
searchInText(abstract.page, keywords) ||
searchInText(abstract.paragraph, keywords)) {
hasMatch = true;
break;
}
}
}
}
// Search in body content (for statutes and legal articles)
if (!hasMatch && (doc.type === 'statute' || doc.type === 'legal_article') && doc.body) {
if (searchInText(doc.body.en, keywords) ||
searchInText(doc.body.dv, keywords)) {
hasMatch = true;
}
}
if (hasMatch) {
matchedDocuments.push(doc);
}
});
let filteredResults = matchedDocuments;
// Apply filters
if (filters.type) {
filteredResults = filteredResults.filter(doc => doc.type === filters.type);
}
if (filters.jurisdiction) {
filteredResults = filteredResults.filter(doc =>
doc.jurisdiction.toLowerCase().includes(filters.jurisdiction.toLowerCase())
);
}
if (filters.dateFrom) {
filteredResults = filteredResults.filter(doc => new Date(doc.date) >= new Date(filters.dateFrom));
}
if (filters.dateTo) {
filteredResults = filteredResults.filter(doc => new Date(doc.date) <= new Date(filters.dateTo));
}
if (jurisdictions.length > 0) {
filteredResults = filteredResults.filter(doc =>
jurisdictions.includes(doc.jurisdiction.toLowerCase())
);
}
setResults(filteredResults);
setActiveTab('search');
// Add to search history
if (query.trim() && !searchHistory.includes(query.trim())) {
setSearchHistory(prev => [query.trim(), ...prev.slice(0, 4)]);
}
showNotification(`Found ${filteredResults.length} results`, 'info');
};
const handleAddDocument = () => {
const { title, dhivehi_title, jurisdiction, date, citation, keywords, judges, introduction, ratio, body, abstracts } = newDoc;
// Validation
if (!jurisdiction.trim() || !date.trim() || !keywords.trim()) {
showNotification("Please fill out all mandatory fields: Jurisdiction, Date, Keywords", 'error');
return;
}
if (!(title.trim() || dhivehi_title.trim())) {
showNotification("Either English or Dhivehi title is required.", 'error');
return;
}
// Type-specific validation
if (newDoc.type === 'case_law') {
if (!(introduction.en.trim() || introduction.dv.trim())) {
showNotification("Either English or Dhivehi Introduction is required.", 'error');
return;
}
if (!(ratio.en.trim() || ratio.dv.trim())) {
showNotification("Either English or Dhivehi Ratio is required for case law documents.", 'error');
return;
}
const validAbstracts = abstracts.filter(ab => ab.en.trim() || ab.dv.trim());
if (validAbstracts.length === 0) {
showNotification("At least one abstract (English or Dhivehi) is required.", 'error');
return;
}
for (let i = 0; i < validAbstracts.length; i++) {
const ab = validAbstracts[i];
if (!ab.page.trim() || !ab.paragraph.trim()) {
showNotification(`Abstract #${i + 1} requires both Page and Paragraph numbers.`, 'error');
return;
}
}
} else if (newDoc.type === 'statute' || newDoc.type === 'legal_article') {
if (!(body.en.trim() || body.dv.trim())) {
showNotification("Either English or Dhivehi Body is required for this document type.", 'error');
return;
}
}
const keywordList = keywords.split(',').map(k => k.trim().toLowerCase()).filter(Boolean);
const judgesList = newDoc.type === 'case_law' && judges ? judges.split(',').map(j => j.trim()).filter(Boolean) : undefined;
const newDocument = {
id: nextId,
type: newDoc.type,
title: title.trim(),
dhivehi_title: dhivehi_title.trim(),
jurisdiction: jurisdiction.trim(),
date: date.trim(),
citation: newDoc.type === 'case_law' ? citation.trim() : undefined,
keywords: keywordList,
judges: judgesList,
introduction: newDoc.type === 'case_law' ? {
en: introduction.en.trim(),
dv: introduction.dv.trim()
} : undefined,
ratio: newDoc.type === 'case_law' ? {
en: ratio.en.trim(),
dv: ratio.dv.trim()
} : undefined,
body: (newDoc.type === 'statute' || newDoc.type === 'legal_article') ? {
en: body.en.trim(),
dv: body.dv.trim()
} : undefined,
abstracts: newDoc.type === 'case_law' ? abstracts.filter(ab => ab.en.trim() || ab.dv.trim()).map(ab => ({
en: ab.en.trim(),
dv: ab.dv.trim(),
page: ab.page.trim(),
paragraph: ab.paragraph.trim()
})) : undefined
};
setLegalDocuments(prev => [...prev, newDocument]);
setNewDoc({
type: 'case_law',
title: '',
dhivehi_title: '',
jurisdiction: '',
date: '',
citation: '',
keywords: '',
judges: '',
introduction: { en: '', dv: '' },
ratio: { en: '', dv: '' },
body: { en: '', dv: '' },
abstracts: [{ en: '', dv: '', page: '', paragraph: '' }]
});
showNotification("Document added successfully!", 'success');
setShowAddForm(false);
};
const removeDocument = (id) => {
setLegalDocuments(prev => prev.filter(doc => doc.id !== id));
showNotification("Document removed successfully.", 'success');
};
const exportData = () => {
const dataStr = JSON.stringify(legalDocuments, null, 2);
const dataUri = 'application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
const exportFileDefaultName = 'aharenge_documents.json';
const linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
};
const clearFilters = () => {
setFilters({ type: '', jurisdiction: '', dateFrom: '', dateTo: '' });
showNotification('Filters cleared', 'info');
};
const addAbstractField = () => {
setNewDoc({
...newDoc,
abstracts: [...newDoc.abstracts, { en: '', dv: '', page: '', paragraph: '' }]
});
};
const updateAbstractField = (index, field, value) => {
const updatedAbstracts = [...newDoc.abstracts];
updatedAbstracts[index][field] = value;
setNewDoc({ ...newDoc, abstracts: updatedAbstracts });
};
const updateIntroField = (lang, value) => {
setNewDoc({
...newDoc,
introduction: { ...newDoc.introduction, [lang]: value }
});
};
const updateRatioField = (lang, value) => {
setNewDoc({
...newDoc,
ratio: { ...newDoc.ratio, [lang]: value }
});
};
const updateBodyField = (lang, value) => {
setNewDoc({
...newDoc,
body: { ...newDoc.body, [lang]: value }
});
};
// Dashboard statistics
const getStatistics = () => {
const totalDocs = legalDocuments.length;
const caseLawCount = legalDocuments.filter(doc => doc.type === 'case_law').length;
const statuteCount = legalDocuments.filter(doc => doc.type === 'statute').length;
const articleCount = legalDocuments.filter(doc => doc.type === 'legal_article').length;
const recentDocs = legalDocuments
.sort((a, b) => new Date(b.date) - new Date(a.date))
.slice(0, 3);
const jurisdictions = [...new Set(legalDocuments.map(doc => doc.jurisdiction))];
return {
totalDocs,
caseLawCount,
statuteCount,
articleCount,
recentDocs,
jurisdictions: jurisdictions.length
};
};
const quickSearchSuggestions = [
{ query: 'contract', description: 'Find contract-related cases' },
{ query: 'California', description: 'Search California jurisdiction' },
{ query: 'employment breach', description: 'Employment law cases' },
{ query: 'privacy data', description: 'Data privacy regulations' }
];
// Loading screen
if (isLoading) {
return (
Loading Aharenge Database...
);
}
// Public View Component
const renderPublicView = () => {
return (
{/* Public Header */}
Aharenge Database
Professional Legal Database
Login with Auth0
Demo Login
{/* Auth Error Display */}
{authError && (
Auth0 Connection Issue
{authError}
Try using "Demo Login" to access the application while troubleshooting Auth0.
setAuthError('')}
className="text-red-900 hover:text-red-700 font-medium px-3 py-1 border border-red-300 rounded"
>
Dismiss
)}
{/* Public Content */}
Professional Legal Database
Comprehensive legal database with bilingual support for legal professionals.
Organize case law, statutes, and legal articles with advanced search capabilities.
{/* Auth0 Configuration Notice */}
Authentication Setup
Auth0 Integration Status:
⚠️ Current Issue: Auth0 domain "ahnaflegaldatabase.au.auth0.com" is not responding
Troubleshooting Steps:
Verify your Auth0 domain is correct in your Auth0 dashboard
Check if your Auth0 application is properly configured
Ensure your Auth0 account is active and the domain exists
Add this URL to your Auth0 application's callback URLs:
{window.location.origin}
Demo Mode: Use the "Demo Login" button for immediate access with admin privileges while troubleshooting Auth0.
{/* Features Grid */}
Advanced Search
Powerful search functionality with filters, keywords, and boolean operators for precise document discovery.
Bilingual Support
Full English and Dhivehi language support for comprehensive legal documentation and accessibility.
Document Organization
Efficiently organize case law, statutes, and legal articles with detailed metadata and categorization.
{/* Call to Action */}
Ready to Get Started?
Access your legal database dashboard and start organizing your case files today.
Login with Auth0
Try Demo Mode
{/* Public Footer */}
);
};
const renderDashboard = () => {
const stats = getStatistics();
return (
{/* Welcome Section */}
Welcome to Aharenge Database
Your comprehensive legal database solution with bilingual support
setActiveTab('search')}
className="bg-white text-black px-6 py-3 rounded-lg font-semibold hover:bg-gray-100 transition-colors"
>
Start Searching
setShowAddForm(true)}
className="border-2 border-white text-white px-6 py-3 rounded-lg font-semibold hover:bg-white hover:text-black transition-colors"
>
Add New Document
{/* Local Storage Status */}
Your documents are automatically saved locally
All documents persist between browser sessions. Use "Backup Database" to save to your computer.
{/* Statistics Cards */}
Total Documents
{stats.totalDocs}
Case Law
{stats.caseLawCount}
Statutes
{stats.statuteCount}
Jurisdictions
{stats.jurisdictions}
{/* Quick Search & Recent Documents */}
{/* Quick Search */}
Quick Search
{quickSearchSuggestions.map((suggestion, index) => (
{
setQuery(suggestion.query);
search();
}}
className="p-4 bg-gray-50 rounded-lg cursor-pointer hover:bg-gray-100 transition-colors"
>
"{suggestion.query}"
{suggestion.description}
))}
{/* Recent Documents */}
Recent Documents
setActiveTab('library')}
className="text-gray-800 hover:text-black text-sm font-medium"
>
View All
{stats.recentDocs.map((doc) => (
setSelectedDoc(doc)}
className="p-4 bg-gray-50 rounded-lg cursor-pointer hover:bg-gray-100 transition-colors"
>
{doc.title || doc.dhivehi_title}
{doc.jurisdiction}
{new Date(doc.date).toLocaleDateString()}
{doc.type === 'case_law' ? 'CASE' : doc.type === 'statute' ? 'STATUTE' : 'ARTICLE'}
))}
{/* System Features */}
System Features
Advanced Search
Powerful search with filters, keywords, and boolean operators
Bilingual Support
Full English and Dhivehi language support for all documents
Database Management
Organize case law, statutes, and legal articles efficiently
);
};
const renderContent = () => {
if (activeTab === 'dashboard') {
return renderDashboard();
} else if (activeTab === 'search') {
return (
{/* Search Bar */}
Search
setShowFilters(!showFilters)}
className="px-4 py-3 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors flex items-center justify-center"
aria-label="Toggle filters"
>
{/* Search History */}
{searchHistory.length > 0 && (
Recent searches:
{searchHistory.map((term, index) => (
setQuery(term)}
className="px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm hover:bg-gray-200 transition-colors"
>
{term}
))}
)}
{/* Advanced Filters */}
{showFilters && (
Advanced Filters
Clear Filters
)}
{/* Search Results */}
Search Results
{results.length > 0 && (
{results.length} documents found
)}
{renderResultsTable(results)}
);
} else if (activeTab === 'library') {
return (
Document Library
{legalDocuments.length} documents total
{currentUser?.role === 'admin' && (
<>
Export CSV
Export PDF
>
)}
{/* Database Backup/Restore Section */}
Export JSON
{renderResultsTable(legalDocuments, currentUser?.role === 'admin')}
);
}
};
const renderResultsTable = (docs, showActions = false) => {
if (docs.length === 0) {
return (
No documents found
Try adjusting your search terms or filters
);
}
return (
Title
Type
Jurisdiction
Date
Keywords
Actions
{docs.map((doc) => (
{doc.title || doc.dhivehi_title}
{doc.citation && (
{doc.citation}
)}
{doc.type === 'case_law' ? 'CASE LAW' : doc.type === 'statute' ? 'STATUTE' : 'LEGAL ARTICLE'}
{doc.jurisdiction}
{new Date(doc.date).toLocaleDateString()}
{doc.keywords.slice(0, 3).map((keyword, idx) => (
{keyword}
))}
{doc.keywords.length > 3 && (
+{doc.keywords.length - 3} more
)}
setSelectedDoc(doc)}
className="text-gray-600 hover:text-gray-900 transition-colors"
title="View Details"
>
{showActions && (
removeDocument(doc.id)}
className="text-red-600 hover:text-red-900 transition-colors"
title="Remove Document"
>
)}
))}
);
};
// If not authenticated, show public view
if (!isAuthenticated) {
return renderPublicView();
}
return (
{/* Load Tailwind and Faruma font */}
{/* Authenticated Header */}
Aharenge Database
Professional Legal Database
setActiveTab('dashboard')}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg transition-colors ${
activeTab === 'dashboard' ? 'bg-gray-800 font-medium' : 'hover:bg-gray-700'
}`}
>
Dashboard
setActiveTab('search')}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg transition-colors ${
activeTab === 'search' ? 'bg-gray-800 font-medium' : 'hover:bg-gray-700'
}`}
>
Search
setActiveTab('library')}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg transition-colors ${
activeTab === 'library' ? 'bg-gray-800 font-medium' : 'hover:bg-gray-700'
}`}
>
Library
setShowAddForm(!showAddForm)}
className="flex items-center space-x-2 px-4 py-2 bg-gray-800 rounded-lg hover:bg-gray-700 transition-colors"
>
{showAddForm ? : }
{showAddForm ? 'Cancel' : 'Add Document'}
{/* User Menu */}
{currentUser?.name}
{currentUser?.role}
{currentUser?.picture && (
)}
{/* Notification */}
{notification.message && (
{notification.message}
)}
{/* Main Content */}
{renderContent()}
{/* Add Document Form */}
{showAddForm && (
Add New Database Entry
setShowAddForm(false)}
className="text-gray-400 hover:text-gray-600 transition-colors"
aria-label="Close form"
>
{/* Document Type */}
Document Type *
setNewDoc({ ...newDoc, type: e.target.value })}
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent"
>
Case Law
Statute
Legal Article
{/* Title Fields */}
{/* Type-Specific Fields */}
{newDoc.type === 'case_law' && (
<>
{/* Citation for Case Law */}
Citation *
setNewDoc({ ...newDoc, citation: e.target.value })}
className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent"
/>
{/* Judges Name */}
{/* Introduction */}
{/* Ratio Decidendi */}
{/* Abstracts */}
Abstracts
{newDoc.abstracts.map((ab, index) => (
Abstract #{index + 1}
{newDoc.abstracts.length > 1 && (
{
const updated = newDoc.abstracts.filter((_, i) => i !== index);
setNewDoc({ ...newDoc, abstracts: updated });
}}
className="text-red-500 hover:text-red-700 transition-colors"
aria-label="Remove abstract"
>
)}
))}
Add Another Abstract
>
)}
{(newDoc.type === 'statute') && (
Statute Details
Body (English)
Body (ދިވެހި)
)}
{(newDoc.type === 'legal_article') && (
Article Content
Body (English)
Body (ދިވެހި)
)}
{/* Common Fields */}
{/* Submit Button */}
setShowAddForm(false)}
className="px-6 py-3 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors w-full sm:w-auto"
>
Cancel
Add Document
)}
{/* Document Detail Modal */}
{selectedDoc && (
{selectedDoc.title || selectedDoc.dhivehi_title}
{selectedDoc.citation || 'N/A'}
{selectedDoc.jurisdiction}
{new Date(selectedDoc.date).toLocaleDateString()}
{selectedDoc.type === 'case_law' ? 'CASE LAW' : selectedDoc.type === 'statute' ? 'STATUTE' : 'LEGAL ARTICLE'}
setSelectedDoc(null)}
className="text-gray-400 hover:text-gray-600 transition-colors self-end"
aria-label="Close details"
>
{/* Keywords */}
Keywords
{selectedDoc.keywords.map((keyword, index) => (
{keyword}
))}
{/* Judges (for Case Law only) */}
{selectedDoc.type === 'case_law' && selectedDoc.judges && selectedDoc.judges.length > 0 && (
Judges
{selectedDoc.judges.map((judge, index) => (
{judge}
))}
)}
{/* Case Law Specific Fields */}
{selectedDoc.type === 'case_law' && (
<>
{/* Introduction */}
Introduction
{selectedDoc.introduction.en || selectedDoc.introduction.dv || 'N/A'}
{/* Ratio Decidendi */}
Ratio Decidendi
{selectedDoc.ratio.en || selectedDoc.ratio.dv || 'N/A'}
{/* Abstracts */}
Abstracts
{selectedDoc.abstracts?.map((ab, index) => (
{ab.page || 'N/A'}
{ab.paragraph || 'N/A'}
{ab.en || ab.dv || 'N/A'}
))}
>
)}
{/* Statute Specific Fields */}
{selectedDoc.type === 'statute' && (
Statute Text
{selectedDoc.body?.en || selectedDoc.body?.dv || 'N/A'}
)}
{/* Legal Article Specific Fields */}
{selectedDoc.type === 'legal_article' && (
Article Content
{selectedDoc.body?.en || selectedDoc.body?.dv || 'N/A'}
)}
)}
{/* Footer */}
);
}