147 lines
5.6 KiB
TypeScript
147 lines
5.6 KiB
TypeScript
import { Router } from "express";
|
|
import { standardsLessons } from "../data/training/standards";
|
|
import { formFactorLessons } from "../data/training/form-factors";
|
|
import { switchesLessons } from "../data/training/switches";
|
|
import { infrastructureLessons } from "../data/training/infrastructure";
|
|
import { testingBuyingLessons } from "../data/training/testing-buying";
|
|
import type { TrainingLesson } from "../data/training/types";
|
|
|
|
export const trainingRouter = Router();
|
|
|
|
// ── Aggregate all lessons ────────────────────────────────────────────────────
|
|
|
|
const ALL_LESSONS: TrainingLesson[] = [
|
|
...standardsLessons,
|
|
...formFactorLessons,
|
|
...switchesLessons,
|
|
...infrastructureLessons,
|
|
...testingBuyingLessons,
|
|
];
|
|
|
|
// ── Category metadata ────────────────────────────────────────────────────────
|
|
|
|
const CATEGORIES = [
|
|
{
|
|
id: "standards",
|
|
title: "Standards",
|
|
title_de: "Standards",
|
|
icon: "📋",
|
|
description: "IEEE 802.3, MSA, DOM/DDMI — the foundation of optical networking",
|
|
description_de: "IEEE 802.3, MSA, DOM/DDMI — das Fundament optischer Netzwerke",
|
|
},
|
|
{
|
|
id: "form-factors",
|
|
title: "Form Factors",
|
|
title_de: "Formfaktoren",
|
|
icon: "🔌",
|
|
description: "SFP, QSFP-DD, OSFP, connectors — physical housings and interfaces",
|
|
description_de: "SFP, QSFP-DD, OSFP, Stecker — physikalische Gehäuse und Interfaces",
|
|
},
|
|
{
|
|
id: "switches",
|
|
title: "Switches & Compatibility",
|
|
title_de: "Switches & Kompatibilität",
|
|
icon: "🖥️",
|
|
description: "Vendor lock, FEC configuration, Cisco / Juniper / Arista CLI",
|
|
description_de: "Vendor-Lock, FEC-Konfiguration, Cisco / Juniper / Arista CLI",
|
|
},
|
|
{
|
|
id: "infrastructure",
|
|
title: "Fiber & Infrastructure",
|
|
title_de: "Glasfaser & Infrastruktur",
|
|
icon: "🔧",
|
|
description: "MMF/SMF grades, link budget math, MPO, WDM wavelength planning",
|
|
description_de: "MMF/SMF-Typen, Link-Budget-Berechnung, MPO, WDM-Wellenlägenplanung",
|
|
},
|
|
{
|
|
id: "testing-buying",
|
|
title: "Testing & Buying",
|
|
title_de: "Testen & Einkaufen",
|
|
icon: "🔬",
|
|
description: "Test equipment, OEM vs. compatible, datasheet interpretation, TCO",
|
|
description_de: "Messgeräte, OEM vs. kompatibel, Datenblatt-Interpretation, TCO",
|
|
},
|
|
];
|
|
|
|
// ── GET /api/training/categories ─────────────────────────────────────────────
|
|
|
|
trainingRouter.get("/categories", (_req, res) => {
|
|
const result = CATEGORIES.map((cat) => ({
|
|
...cat,
|
|
lesson_count: ALL_LESSONS.filter((l) => l.category === cat.id).length,
|
|
total_duration_min: ALL_LESSONS
|
|
.filter((l) => l.category === cat.id)
|
|
.reduce((sum, l) => sum + l.duration_min, 0),
|
|
quiz_count: ALL_LESSONS
|
|
.filter((l) => l.category === cat.id)
|
|
.reduce((sum, l) => sum + l.quiz.length, 0),
|
|
}));
|
|
res.json(result);
|
|
});
|
|
|
|
// ── GET /api/training/lessons?category= ──────────────────────────────────────
|
|
|
|
trainingRouter.get("/lessons", (req, res) => {
|
|
const { category } = req.query;
|
|
let lessons = ALL_LESSONS;
|
|
if (category && typeof category === "string") {
|
|
lessons = lessons.filter((l) => l.category === category);
|
|
}
|
|
// Return metadata only — omit heavy sections + quiz content
|
|
res.json(
|
|
lessons.map(({ id, category, title, title_de, level, duration_min, summary, summary_de, tags }) => ({
|
|
id,
|
|
category,
|
|
title,
|
|
title_de,
|
|
level,
|
|
duration_min,
|
|
summary,
|
|
summary_de,
|
|
tags,
|
|
}))
|
|
);
|
|
});
|
|
|
|
// ── GET /api/training/lessons/:id ────────────────────────────────────────────
|
|
|
|
trainingRouter.get("/lessons/:id", (req, res) => {
|
|
const lesson = ALL_LESSONS.find((l) => l.id === req.params.id);
|
|
if (!lesson) {
|
|
return res.status(404).json({ error: "Lesson not found" });
|
|
}
|
|
return res.json(lesson);
|
|
});
|
|
|
|
// ── GET /api/training/quiz?lesson=&category= ─────────────────────────────────
|
|
|
|
trainingRouter.get("/quiz", (req, res) => {
|
|
const { lesson, category } = req.query;
|
|
let questions = ALL_LESSONS.flatMap((l) => l.quiz);
|
|
|
|
if (lesson && typeof lesson === "string") {
|
|
questions = questions.filter((q) => q.lesson === lesson);
|
|
} else if (category && typeof category === "string") {
|
|
const catLessonIds = ALL_LESSONS.filter((l) => l.category === category).map((l) => l.id);
|
|
questions = questions.filter((q) => catLessonIds.includes(q.lesson));
|
|
}
|
|
|
|
res.json(questions);
|
|
});
|
|
|
|
// ── GET /api/training/stats ───────────────────────────────────────────────────
|
|
|
|
trainingRouter.get("/stats", (_req, res) => {
|
|
res.json({
|
|
total_lessons: ALL_LESSONS.length,
|
|
total_quiz_questions: ALL_LESSONS.reduce((s, l) => s + l.quiz.length, 0),
|
|
total_duration_min: ALL_LESSONS.reduce((s, l) => s + l.duration_min, 0),
|
|
categories: CATEGORIES.length,
|
|
levels: {
|
|
beginner: ALL_LESSONS.filter((l) => l.level === "beginner").length,
|
|
intermediate: ALL_LESSONS.filter((l) => l.level === "intermediate").length,
|
|
advanced: ALL_LESSONS.filter((l) => l.level === "advanced").length,
|
|
},
|
|
});
|
|
});
|