Files
tickte-system/src/App.jsx
T

131 lines
5.8 KiB
React

import { useCallback, useEffect, useState } from 'react'
import { BrowserRouter, Routes, Route, Navigate, Link, useLocation } from 'react-router-dom'
import { AuthProvider, useAuth } from './context/AuthContext'
import Sidebar from './components/Navbar'
import Logo from './components/Logo'
import LoginPage from './pages/LoginPage'
import TicketsPage from './pages/TicketsPage'
import TicketPage from './pages/TicketPage'
import PlanboardPage from './pages/PlanboardPage'
import ProjectsPage from './pages/ProjectsPage'
import AdminPage from './pages/AdminPage'
import CustomersPage from './pages/CustomersPage'
import CustomerDetailPage from './pages/CustomerDetailPage'
import CustomerChatPage from './pages/CustomerChatPage'
import StorageTasksPage from './pages/StorageTasksPage'
import CreditsPage from './pages/CreditsPage'
import OffersPage from './pages/OffersPage'
import FinancePage from './pages/FinancePage'
import FinanceSettingsPage from './pages/FinanceSettingsPage'
import LegalPage from './pages/LegalPage'
import AssignProjectPage from './pages/AssignProjectPage'
import ServicesPage from './pages/ServicesPage'
import ServerpulsPage from './pages/ServerpulsPage'
import BankCallbackPage from './pages/BankCallbackPage'
import AdminRoute from './components/AdminRoute'
import { safeReturnPath } from './lib/returnPath'
function ProtectedRoute({ children }) {
const { user, loading } = useAuth()
const location = useLocation()
if (loading) {
return <div className="loading">Wird geladen …</div>
}
if (!user) {
return <Navigate to="/login" state={{ from: location }} replace />
}
return children
}
function AppRoutes() {
const { user } = useAuth()
const location = useLocation()
return (
<Routes>
<Route path="/login" element={user ? <Navigate to={safeReturnPath(location.state?.from)} replace /> : <LoginPage />} />
<Route path="/" element={<Navigate to="/tickets" replace />} />
<Route path="/tickets" element={<ProtectedRoute><TicketsPage /></ProtectedRoute>} />
<Route path="/tickets/:ref/:tab?" element={<ProtectedRoute><TicketPage /></ProtectedRoute>} />
<Route path="/customers" element={<ProtectedRoute><CustomersPage /></ProtectedRoute>} />
{/* Messenger (Kunden -> Chat); statische Pfade gehen in react-router vor /customers/:id */}
<Route path="/customers/chat" element={<ProtectedRoute><CustomerChatPage /></ProtectedRoute>} />
<Route path="/customers/email-speicher" element={<ProtectedRoute><StorageTasksPage /></ProtectedRoute>} />
<Route path="/customers/gutschriften" element={<ProtectedRoute><CreditsPage /></ProtectedRoute>} />
<Route path="/customers/angebote" element={<ProtectedRoute><OffersPage /></ProtectedRoute>} />
<Route path="/customers/:id" element={<ProtectedRoute><CustomerDetailPage /></ProtectedRoute>} />
<Route path="/leads" element={<Navigate to="/tickets" replace />} />
{/* Finanzen: nur Admins, alle anderen gehen zu /tickets */}
<Route path="/finance" element={<ProtectedRoute><AdminRoute><FinancePage /></AdminRoute></ProtectedRoute>} />
<Route path="/finance/einstellungen" element={<ProtectedRoute><AdminRoute><FinanceSettingsPage /></AdminRoute></ProtectedRoute>} />
<Route path="/finanzen/bank-callback" element={<ProtectedRoute><AdminRoute><BankCallbackPage /></AdminRoute></ProtectedRoute>} />
<Route path="/rechtliches" element={<ProtectedRoute><LegalPage /></ProtectedRoute>} />
<Route path="/serverpuls" element={<ProtectedRoute><ServerpulsPage /></ProtectedRoute>} />
<Route path="/planboard" element={<ProtectedRoute><PlanboardPage /></ProtectedRoute>} />
<Route path="/projects" element={<ProtectedRoute><ProjectsPage /></ProtectedRoute>} />
<Route path="/services" element={<ProtectedRoute><ServicesPage /></ProtectedRoute>} />
<Route path="/projects/zuordnen/:id" element={<ProtectedRoute><AssignProjectPage /></ProtectedRoute>} />
<Route path="/admin" element={<ProtectedRoute><AdminPage /></ProtectedRoute>} />
{/* Alte Links (z. B. die entfernten Seiten Assets, Reports, Docs, Roadmaps) landen bei den Tickets */}
<Route path="*" element={<Navigate to="/tickets" replace />} />
</Routes>
)
}
function AppContent() {
const { user } = useAuth()
const location = useLocation()
// Schublade des Menues auf dem Handy; auf dem Desktop steht das Menue fest und menuOpen bleibt ohne Wirkung
const [menuOpen, setMenuOpen] = useState(false)
const open = useCallback(() => setMenuOpen(true), [])
const close = useCallback(() => setMenuOpen(false), [])
// Seitenwechsel (nur der Pfad, nicht ?woid= oder ?tab=): Schublade zu und wieder nach oben
useEffect(() => {
setMenuOpen(false)
window.scrollTo(0, 0)
}, [location.pathname])
// Esc schliesst die Schublade, nur solange sie offen ist und nur, wenn kein anderer Baustein die Taste schon behandelt hat
useEffect(() => {
if (!menuOpen) return undefined
const onKey = (e) => {
if (e.defaultPrevented) return
if (e.key === 'Escape') setMenuOpen(false)
}
document.addEventListener('keydown', onKey)
return () => document.removeEventListener('keydown', onKey)
}, [menuOpen])
if (!user) {
return <AppRoutes />
}
return (
<div className={`shell${menuOpen ? ' menu-open' : ''}`}>
<Sidebar onNavigate={close} />
<div className="scrim" onClick={close} />
<div className="main">
<div className="mobile-bar">
<Link className="brand" to="/tickets"><Logo />WEBklar</Link>
<button type="button" className="btn btn-quiet" onClick={open} aria-expanded={menuOpen} aria-controls="menue">Menü</button>
</div>
<main id="inhalt"><AppRoutes /></main>
</div>
</div>
)
}
export default function App() {
return (
<BrowserRouter>
<AuthProvider>
<AppContent />
</AuthProvider>
</BrowserRouter>
)
}