diff --git a/apps/teacher-portal/src/app/(app)/classes/page.tsx b/apps/teacher-portal/src/app/(app)/classes/page.tsx new file mode 100644 index 0000000..3b9d025 --- /dev/null +++ b/apps/teacher-portal/src/app/(app)/classes/page.tsx @@ -0,0 +1,294 @@ +"use client"; + +import { useState, useEffect, useCallback } from "react"; +import { getToken } from "@/lib/auth"; + +interface ClassItem { + id: string; + name: string; + gradeId: string; + description?: string; + createdAt: number; + updatedAt: number; +} + +interface ApiResponse { + success: boolean; + data?: T; + error?: { code: string; message: string }; +} + +export default function ClassesPage() { + const [classes, setClasses] = useState([]); + const [loading, setLoading] = useState(false); + const [name, setName] = useState(""); + const [gradeId, setGradeId] = useState( + "550e8400-e29b-41d4-a716-446655440000", + ); + const [description, setDescription] = useState(""); + const [error, setError] = useState(null); + + const authHeaders = (): Record => { + const token = getToken(); + return token ? { Authorization: `Bearer ${token}` } : {}; + }; + + const fetchClasses = useCallback(async () => { + setLoading(true); + setError(null); + try { + const res = await fetch("/api/v1/classes", { + headers: authHeaders(), + }); + const json: ApiResponse = await res.json(); + if (json.success && json.data) { + setClasses(json.data); + } else { + setError(json.error?.message || "Failed to load"); + } + } catch (e) { + setError(e instanceof Error ? e.message : "Network error"); + } finally { + setLoading(false); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + fetchClasses(); + }, [fetchClasses]); + + const handleCreate = async (e: React.FormEvent) => { + e.preventDefault(); + if (!name.trim()) return; + try { + const res = await fetch("/api/v1/classes", { + method: "POST", + headers: { + "Content-Type": "application/json", + ...authHeaders(), + }, + body: JSON.stringify({ name, gradeId, description }), + }); + const json = await res.json(); + if (!json.success) { + setError(json.error?.message || "Create failed"); + return; + } + setName(""); + setDescription(""); + await fetchClasses(); + } catch (e) { + setError(e instanceof Error ? e.message : "Network error"); + } + }; + + const handleDelete = async (id: string) => { + try { + const res = await fetch(`/api/v1/classes/${id}`, { + method: "DELETE", + headers: authHeaders(), + }); + const json = await res.json(); + if (!json.success) { + setError(json.error?.message || "Delete failed"); + return; + } + await fetchClasses(); + } catch (e) { + setError(e instanceof Error ? e.message : "Network error"); + } + }; + + return ( +
+
+

+ 班级管理 +

+

+ classes 域 CRUD · JWT 鉴权 +

+
+ +
+ +
+