6.9 KiB
Technische Dokumentation: Frontend Architektur (IHK/Ausbildung)
1. Einleitung & Technologiewahl
Diese Dokumentation beschreibt die Frontend-Architektur des IT-Ticketsystems. Ziel ist es, die Funktionsweise, die verwendeten Patterns und die TypeScript-spezifische Implementierung für die IHK-Abschlussprüfung verständlich darzulegen.
Techn Stack:
- Framework: Next.js (React)
- Begründung: Next.js bietet "Server-Side Rendering" (SSR) und einfaches Routing, was die Performance und SEO verbessert. Es strukturiert React-Anwendungen klar.
- Sprache: TypeScript
- Begründung: Typsicherheit minimiert Laufzeitfehler. Interfaces (
User,Ticket) definieren klare Datenstrukturen, was besonders bei Teamarbeit und Wartung essenziell ist.
- Begründung: Typsicherheit minimiert Laufzeitfehler. Interfaces (
- State Management: React Context API
- Begründung: Vermeidung von "Prop Drilling" (Weiterreichen von Daten über viele Ebenen). Zustände wie
useroderticketswerden global verfügbar gemacht.
- Begründung: Vermeidung von "Prop Drilling" (Weiterreichen von Daten über viele Ebenen). Zustände wie
2. Projektstruktur & Scopes
Die Dateien sind logisch getrennt:
app/: Enthält die Seiten (Pages) und Routen. (Next.js App Router).components/: Enthält wiederverwendbare UI-Elemente (Buttons, Cards, Tabellen).lib/: Enthält Logik, Typen und Konfigurationen (z.B.auth-context.tsx,types.ts).
TypeScript Besonderheit: Interfaces (lib/types.ts)
In JavaScript wüssten wir nicht, welche Eigenschaften ein "Ticket" hat. In TypeScript definieren wir einen Bauplan (Interface):
// lib/types.ts
export type TicketStatus = "OPEN" | "IN_PROGRESS" | "CLOSED" // Union Type: Nur diese 3 Werte sind erlaubt
export interface User {
id: number;
name: string;
role: "LEHRKRAFT" | "RAUMBETREUER"; // Enforced Role
supervisedRooms?: Room[]; // Optionales Array (?): Nicht jeder User hat Räume
}
export interface Ticket {
id: number;
description: string;
status: TicketStatus; // Verwendet den Type von oben
owner: User; // Referenziert das User Interface
}
Warum? Wenn ich nun ticket.statu schreibe, warnt mich VS Code sofort ("Did you mean 'status'?"). Das verhindert Tippfehler und Logikfehler.
3. Kernlogik: Der AuthContext (lib/auth-context.tsx)
Das "Gehirn" der Anwendung. Hier passiert die Kommunikation mit dem Backend.
Konzept: Provider Pattern
Der AuthProvider ist eine Komponente, die um die gesamte App gewickelt wird. Alle Komponenten innerhalb dieses Providers können auf seine Daten zugreifen.
Code-Analyse (vereinfacht):
// Definition des Context-Typs (Was bietet der Context an?)
interface AuthContextType {
user: User | null; // Kann null sein, wenn ausgeloggt
tickets: Ticket[];
login: (e: string, p: string) => Promise<boolean>; // Asynchrone Funktion, gibt true/false zurück
}
// Erstellen des Contexts
const AuthContext = createContext<AuthContextType | null>(null);
// Der Provider (Die Komponente, die Daten bereitstellt)
export function AuthProvider({ children }: { children: ReactNode }) {
// Lokaler State im Provider
const [user, setUser] = useState<User | null>(null); // Generics: <User | null>
// Funktion zum Daten holen
const fetchTickets = useCallback(async (authHeader: string) => {
// API Call
const res = await fetch(\`\${process.env.NEXT_PUBLIC_API_URL}/tickets\`, {
headers: { Authorization: authHeader }
});
const data = await res.json();
setTickets(data); // TypeScript weiß: data muss Ticket[] sein
}, []); // Empty dependency array: Funktion wird nicht ständig neu erstellt
// Diese Werte werden "nach unten" gegeben
return (
<AuthContext.Provider value={{ user, tickets, login, /*...*/ }}>
{children}
</AuthContext.Provider>
);
}
Erklärung für die Doku:
- State: Wir nutzen
useState, um Daten zu speichern. TypeScript sorgt dafür, dass wir inticketsnur echte Tickets speichern können. - Generics:
useState<User | null>sagt: "Dieser State darf nur ein User-Objekt oder null sein". - Hooks:
useEffect: "Mach das, wenn die App lädt" (z.B. User prüfen).useCallback: Speichert eine Funktion, damit sie nicht bei jedem Neuladen der Seite neu erstellt wird (Performance).
4. Komponenten-Logik (Beispiel: SupervisorDashboard)
Hier sehen wir, wie die Daten konsumiert werden.
export function SupervisorDashboard() {
// 1. Hook nutzen: Zugriff auf globale Daten
const { user, tickets } = useAuth();
// 2. Optional Chaining (?.)
// Wenn user null ist, stürzt die App nicht ab, sondern gibt undefined zurück.
// || [] bedeutet: Wenn undefined/null, nutze leeres Array.
const supervisedRooms = user?.supervisedRooms || [];
// 3. Computed Values (useMemo)
// Berechne 'roomTickets' nur neu, wenn sich 'tickets' oder 'supervisedRooms' ändern.
const roomTickets = useMemo(() => {
if (!supervisedRooms.length) return [];
// Array Methoden: map() und filter()
const roomIds = supervisedRooms.map(r => r.id); // [101, 102]
return tickets.filter((t) => roomIds.includes(t.room.id));
}, [tickets, supervisedRooms]);
return (
<div>
{/* Conditional Rendering: Zeige Badge nur wenn count > 0 */}
{stats.open > 0 && <Badge>{stats.open}</Badge>}
</div>
);
}
Wichtige Konzepte:
- Destructuring:
const { user } = useAuth()zieht nur dieuser-Variable aus dem Context. - Array High-Order Functions:
map,filter,reducesind Standard in modernem JS/TS, um Daten umzuwandeln. - Dependency Arrays:
[tickets, supervisedRooms]sagt React: "Führe diese Berechnung nur aus, wenn sich EINE dieser Variablen geändert hat".
5. Authentifizierung (NextAuth & Middleware)
Ablauf:
- User gibt Daten ein ->
signIn("credentials", ...) - NextAuth sendet Request an unser Backend (
route.ts). - Backend antwortet: "OK, hier ist der User".
- Frontend speichert User in der Session.
- Der
NextAuthSessionProviderinlayout.tsxhält die Session aktiv.
Code (route.ts):
async authorize(credentials) {
// API Anfrage an eigenes Java Backend
const res = await fetch(API_URL + "/login", { ...body });
if (res.ok) {
const user = await res.json();
return user; // NextAuth speichert diesen User
}
return null; // Login fehlgeschlagen
}
Wir nutzen NextAuth als "Mittelsmann". Es kümmert sich um Cookies und Security, während unser Backend die eigentliche Prüfung macht.
Zusammenfassung für die Präsentation
"Die Architektur basiert auf einer klaren Trennung von UI (Components) und Logik (Context/Hooks). Durch den Einsatz von TypeScript werden Datenstrukturen wie User und Tickets strikt typisiert, was die Fehleranfälligkeit drastisch reduziert. Das State Management erfolgt effizient über React Context, wodurch alle Komponenten stets synchronen Zugriff auf den aktuellen Anwendungszustand haben, ohne dass Daten mühsam durchgereicht werden müssen (Prop Drilling)."