Some checks failed
CI / skip-ci-check (pull_request) Successful in 8s
CI / lint-and-type-check (pull_request) Successful in 54s
CI / test-backend (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
CI / secret-scanning (pull_request) Has been cancelled
CI / dependency-scan (pull_request) Has been cancelled
CI / sast-scan (pull_request) Has been cancelled
CI / workflow-summary (pull_request) Has been cancelled
CI / python-lint (pull_request) Has been cancelled
- Add backend click logging utility with file rotation and retention - Add POST /api/v1/log/click endpoint for logging click events - Add frontend click logger service with batching and context extraction - Add global click handler in App.tsx for authenticated users - Add log cleanup script for old log files - Update QUICK_LOG_REFERENCE.md with click log documentation Logs are written to /opt/punimtag/logs/admin-clicks.log with: - Auto-rotation at 10MB (keeps 5 backups) - 30-day retention - Format: timestamp | username | page | element_type | element_id | element_text | context
175 lines
5.0 KiB
TypeScript
175 lines
5.0 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
|
|
import { AuthProvider, useAuth } from './context/AuthContext'
|
|
import { DeveloperModeProvider } from './context/DeveloperModeContext'
|
|
import Login from './pages/Login'
|
|
import Dashboard from './pages/Dashboard'
|
|
import Search from './pages/Search'
|
|
import Scan from './pages/Scan'
|
|
import Process from './pages/Process'
|
|
import Identify from './pages/Identify'
|
|
import AutoMatch from './pages/AutoMatch'
|
|
import Modify from './pages/Modify'
|
|
import Tags from './pages/Tags'
|
|
import FacesMaintenance from './pages/FacesMaintenance'
|
|
import ApproveIdentified from './pages/ApproveIdentified'
|
|
import ManageUsers from './pages/ManageUsers'
|
|
import ReportedPhotos from './pages/ReportedPhotos'
|
|
import PendingPhotos from './pages/PendingPhotos'
|
|
import UserTaggedPhotos from './pages/UserTaggedPhotos'
|
|
import ManagePhotos from './pages/ManagePhotos'
|
|
import Settings from './pages/Settings'
|
|
import Help from './pages/Help'
|
|
import Layout from './components/Layout'
|
|
import PasswordChangeModal from './components/PasswordChangeModal'
|
|
import AdminRoute from './components/AdminRoute'
|
|
import { logClick, flushPendingClicks } from './services/clickLogger'
|
|
|
|
function PrivateRoute({ children }: { children: React.ReactNode }) {
|
|
const { isAuthenticated, isLoading, passwordChangeRequired } = useAuth()
|
|
const [showPasswordModal, setShowPasswordModal] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (isAuthenticated && passwordChangeRequired) {
|
|
setShowPasswordModal(true)
|
|
}
|
|
}, [isAuthenticated, passwordChangeRequired])
|
|
|
|
if (isLoading) {
|
|
return <div className="min-h-screen flex items-center justify-center">Loading...</div>
|
|
}
|
|
|
|
if (!isAuthenticated) {
|
|
return <Navigate to="/login" replace />
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{showPasswordModal && (
|
|
<PasswordChangeModal
|
|
onSuccess={() => {
|
|
setShowPasswordModal(false)
|
|
}}
|
|
/>
|
|
)}
|
|
{children}
|
|
</>
|
|
)
|
|
}
|
|
|
|
function AppRoutes() {
|
|
const { isAuthenticated } = useAuth()
|
|
|
|
// Set up global click logging for authenticated users
|
|
useEffect(() => {
|
|
if (!isAuthenticated) {
|
|
return
|
|
}
|
|
|
|
const handleClick = (event: MouseEvent) => {
|
|
const target = event.target as HTMLElement
|
|
if (target) {
|
|
logClick(target)
|
|
}
|
|
}
|
|
|
|
// Add click listener
|
|
document.addEventListener('click', handleClick, true) // Use capture phase
|
|
|
|
// Flush pending clicks on page unload
|
|
const handleBeforeUnload = () => {
|
|
flushPendingClicks()
|
|
}
|
|
window.addEventListener('beforeunload', handleBeforeUnload)
|
|
|
|
return () => {
|
|
document.removeEventListener('click', handleClick, true)
|
|
window.removeEventListener('beforeunload', handleBeforeUnload)
|
|
// Flush any pending clicks on cleanup
|
|
flushPendingClicks()
|
|
}
|
|
}, [isAuthenticated])
|
|
|
|
return (
|
|
<Routes>
|
|
<Route path="/login" element={<Login />} />
|
|
<Route
|
|
path="/"
|
|
element={
|
|
<PrivateRoute>
|
|
<Layout />
|
|
</PrivateRoute>
|
|
}
|
|
>
|
|
<Route index element={<Dashboard />} />
|
|
<Route path="scan" element={<Scan />} />
|
|
<Route path="process" element={<Process />} />
|
|
<Route path="search" element={<Search />} />
|
|
<Route path="identify" element={<Identify />} />
|
|
<Route path="auto-match" element={<AutoMatch />} />
|
|
<Route path="modify" element={<Modify />} />
|
|
<Route path="tags" element={<Tags />} />
|
|
<Route path="manage-photos" element={<ManagePhotos />} />
|
|
<Route path="faces-maintenance" element={<FacesMaintenance />} />
|
|
<Route
|
|
path="approve-identified"
|
|
element={
|
|
<AdminRoute featureKey="user_identified">
|
|
<ApproveIdentified />
|
|
</AdminRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="manage-users"
|
|
element={
|
|
<AdminRoute featureKey="manage_users">
|
|
<ManageUsers />
|
|
</AdminRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="reported-photos"
|
|
element={
|
|
<AdminRoute featureKey="user_reported">
|
|
<ReportedPhotos />
|
|
</AdminRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="pending-linkages"
|
|
element={
|
|
<AdminRoute featureKey="user_tagged">
|
|
<UserTaggedPhotos />
|
|
</AdminRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="pending-photos"
|
|
element={
|
|
<AdminRoute featureKey="user_uploaded">
|
|
<PendingPhotos />
|
|
</AdminRoute>
|
|
}
|
|
/>
|
|
<Route path="settings" element={<Settings />} />
|
|
<Route path="help" element={<Help />} />
|
|
</Route>
|
|
</Routes>
|
|
)
|
|
}
|
|
|
|
function App() {
|
|
return (
|
|
<AuthProvider>
|
|
<DeveloperModeProvider>
|
|
<BrowserRouter>
|
|
<AppRoutes />
|
|
</BrowserRouter>
|
|
</DeveloperModeProvider>
|
|
</AuthProvider>
|
|
)
|
|
}
|
|
|
|
export default App
|
|
|