Performance: Code-Splitting pro Seite, KI-Chat lazy, Bilder als WebP (Einstieg 1071->285 kB)
This commit is contained in:
+20
-15
@@ -1,22 +1,25 @@
|
||||
import { Routes, Route, Navigate } from 'react-router'
|
||||
import { Component, type ReactNode } from 'react'
|
||||
import { Component, Suspense, lazy, type ReactNode } from 'react'
|
||||
import { StoreProvider, useStore } from './store/store'
|
||||
import AppLayout from './components/AppLayout'
|
||||
import Login from './pages/Login'
|
||||
import Dashboard from './pages/Dashboard'
|
||||
import Payments from './pages/Payments'
|
||||
import Tickets from './pages/Tickets'
|
||||
import Documents from './pages/Documents'
|
||||
import Messages from './pages/Messages'
|
||||
import Zaehler from './pages/Zaehler'
|
||||
import Kosten from './pages/Kosten'
|
||||
import Bestand from './pages/Bestand'
|
||||
import Mietvertraege from './pages/Mietvertraege'
|
||||
import MieterVerwaltung from './pages/MieterVerwaltung'
|
||||
import Assistent from './pages/Assistent'
|
||||
import BenutzerVerwaltung from './pages/BenutzerVerwaltung'
|
||||
import Profil from './pages/Profil'
|
||||
import Anleitung from './pages/Anleitung'
|
||||
|
||||
// Seiten werden beim ersten Aufruf nachgeladen (Code-Splitting) –
|
||||
// Verwaltungsmodule landen nicht im Bundle der Mieter.
|
||||
const Dashboard = lazy(() => import('./pages/Dashboard'))
|
||||
const Payments = lazy(() => import('./pages/Payments'))
|
||||
const Tickets = lazy(() => import('./pages/Tickets'))
|
||||
const Documents = lazy(() => import('./pages/Documents'))
|
||||
const Messages = lazy(() => import('./pages/Messages'))
|
||||
const Zaehler = lazy(() => import('./pages/Zaehler'))
|
||||
const Kosten = lazy(() => import('./pages/Kosten'))
|
||||
const Bestand = lazy(() => import('./pages/Bestand'))
|
||||
const Mietvertraege = lazy(() => import('./pages/Mietvertraege'))
|
||||
const MieterVerwaltung = lazy(() => import('./pages/MieterVerwaltung'))
|
||||
const Assistent = lazy(() => import('./pages/Assistent'))
|
||||
const BenutzerVerwaltung = lazy(() => import('./pages/BenutzerVerwaltung'))
|
||||
const Profil = lazy(() => import('./pages/Profil'))
|
||||
const Anleitung = lazy(() => import('./pages/Anleitung'))
|
||||
|
||||
/** Fängt Render-Fehler ab – statt schwarzem Bildschirm: Hinweis + Reload. */
|
||||
class FehlerGrenze extends Component<{ children: ReactNode }, { fehler: boolean }> {
|
||||
@@ -76,6 +79,7 @@ export default function App() {
|
||||
return (
|
||||
<FehlerGrenze>
|
||||
<StoreProvider>
|
||||
<Suspense fallback={<Ladebildschirm />}>
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginGate />} />
|
||||
<Route
|
||||
@@ -102,6 +106,7 @@ export default function App() {
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</StoreProvider>
|
||||
</FehlerGrenze>
|
||||
)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 148 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -1,9 +1,11 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Suspense, lazy, useEffect, useState } from 'react'
|
||||
import { useLocation } from 'react-router'
|
||||
import { Sparkles, X } from 'lucide-react'
|
||||
import AssistChat from './AssistChat'
|
||||
import { useStore } from '../../store/store'
|
||||
|
||||
// Chat-Code erst laden, wenn die Verwaltung den Assistenten das erste Mal öffnet
|
||||
const AssistChat = lazy(() => import('./AssistChat'))
|
||||
|
||||
/** KI-Assistent als einblendbares Widget (v1.38):
|
||||
* Vertikale Lasche am rechten Rand öffnet ein Chat-Panel über der aktuellen Seite.
|
||||
* Nur für die Verwaltung (role admin); auf der Assistenten-Seite selbst ausgeblendet. */
|
||||
@@ -11,6 +13,9 @@ export default function AssistWidget() {
|
||||
const { role } = useStore()
|
||||
const location = useLocation()
|
||||
const [offen, setOffen] = useState(false)
|
||||
// Chat bleibt nach dem ersten Öffnen gemountet (Verlauf erhalten), wird aber
|
||||
// vorher nicht geladen – der Chunk kommt erst bei Bedarf über die Leitung.
|
||||
const [warOffen, setWarOffen] = useState(false)
|
||||
|
||||
// Bei Seitenwechsel Panel schließen (außer manuell wieder geöffnet)
|
||||
useEffect(() => {
|
||||
@@ -34,7 +39,10 @@ export default function AssistWidget() {
|
||||
{/* Lasche am rechten Rand */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOffen(true)}
|
||||
onClick={() => {
|
||||
setWarOffen(true)
|
||||
setOffen(true)
|
||||
}}
|
||||
aria-label="KI-Assistent öffnen"
|
||||
className={`fixed right-0 top-1/2 z-40 flex -translate-y-1/2 flex-col items-center gap-2 rounded-l-xl bg-teal-600 py-4 pl-2.5 pr-2 text-white shadow-lg transition-all hover:bg-teal-500 hover:pr-3 ${
|
||||
offen ? 'pointer-events-none translate-x-full opacity-0' : 'translate-x-0 opacity-100'
|
||||
@@ -76,7 +84,11 @@ export default function AssistWidget() {
|
||||
</header>
|
||||
|
||||
<div className="flex min-h-0 flex-1 flex-col p-4">
|
||||
<AssistChat />
|
||||
{warOffen ? (
|
||||
<Suspense fallback={<div className="py-8 text-center text-sm text-slate-400">Assistent wird geladen …</div>}>
|
||||
<AssistChat />
|
||||
</Suspense>
|
||||
) : null}
|
||||
<p className="mt-2 text-[11px] leading-snug text-slate-400 dark:text-slate-500">
|
||||
Antworten können fehlerhaft sein – kritische Daten bitte prüfen.
|
||||
</p>
|
||||
|
||||
+4
-3
@@ -2,8 +2,8 @@ import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { KeyRound, MailPlus, Sparkles, UserCheck } from 'lucide-react'
|
||||
import logo from '../assets/hinterstein/logo.png'
|
||||
import hero from '../assets/hero.jpg'
|
||||
import joseph from '../assets/hinterstein/joseph-hinterstein.jpg'
|
||||
import hero from '../assets/hero.webp'
|
||||
import joseph from '../assets/hinterstein/joseph-hinterstein.webp'
|
||||
import kontakt from '../assets/hinterstein/kontakt.jpg'
|
||||
import { useStore, DEMO_PASSWORT } from '../store/store'
|
||||
|
||||
@@ -91,6 +91,7 @@ export default function Login() {
|
||||
<img
|
||||
src={joseph}
|
||||
alt="Joseph Hinterstein, Ihr Ansprechpartner"
|
||||
loading="lazy"
|
||||
className="h-32 w-24 rounded-2xl object-cover object-top"
|
||||
/>
|
||||
<div className="text-sm text-slate-100">
|
||||
@@ -103,7 +104,7 @@ export default function Login() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img src={kontakt} alt="Kontakt: joseph@hinterstein-immo.de" className="w-full rounded-2xl shadow-lg" />
|
||||
<img src={kontakt} alt="Kontakt: joseph@hinterstein-immo.de" loading="lazy" className="w-full rounded-2xl shadow-lg" />
|
||||
</div>
|
||||
|
||||
{/* Rechte Spalte: Login-/Registrier-Karte */}
|
||||
|
||||
Reference in New Issue
Block a user