Rene Fichtmueller bfb43809a8 feat: standards audit + form factors reference
- Migration 100: adds `description` column to standards (plain-language
  DE·EN for non-technical colleagues), fills all 63 standards incl.
  complete 200G tier (SR4/DR4/FR4/LR4/ER4/CR4), copper DAC variants,
  PON family (GPON/XG-PON1/NG-PON2/25G-PON), 1.6T emerging standard

- Migration 101: new form_factors table — 20 entries covering SFP family
  (SFP→SFP112), QSFP family (QSFP+→QSFP-DD800), OSFP family (OSFP→OSFP224),
  CFP family, legacy XFP/CXP with full_name, speed, channels, status,
  supersedes chain, and bilingual plain-language descriptions

- GET /api/form-factors — new endpoint, returns all form factors with
  transceiver_count join
- GET /api/form-factors/:name — single form factor detail

Dashboard Standards tab:
  - DB description shown as subtitle in standards table rows
  - Full DE + EN description in standard detail panel
  - New Form Factors grid section with status badges, speed, channel info,
    family color coding, supersedes chain
  - openFormFactorDetail() panel with full specs + transceiver link
  - Search extended to match description + notes fields
2026-04-25 20:58:45 +02:00

74 lines
2.4 KiB
TypeScript

/**
* Form Factors API
* ─────────────────────────────────────────────────────────────────────────────
* Routes:
* GET /api/form-factors — All form factors with descriptions
* GET /api/form-factors/:name — Single form factor by name (e.g. QSFP28)
*/
import { Router, Request, Response } from "express";
import { pool } from "../db/client";
export const formFactorsRouter = Router();
// GET /api/form-factors
formFactorsRouter.get("/", async (_req: Request, res: Response) => {
try {
const { rows } = await pool.query(`
SELECT
ff.*,
COUNT(t.id)::int AS transceiver_count
FROM form_factors ff
LEFT JOIN transceivers t ON t.form_factor = ff.name
GROUP BY ff.id
ORDER BY
CASE ff.status
WHEN 'current' THEN 1
WHEN 'emerging' THEN 2
WHEN 'legacy' THEN 3
WHEN 'obsolete' THEN 4
ELSE 5
END,
ff.max_speed_gbps DESC NULLS LAST,
ff.name
`);
res.json({ success: true, data: rows, total: rows.length });
} catch (err) {
console.error("GET /api/form-factors error:", err);
res.status(500).json({ success: false, error: "Internal server error" });
}
});
// GET /api/form-factors/:name
formFactorsRouter.get("/:name", async (req: Request, res: Response) => {
try {
const { rows } = await pool.query(`
SELECT
ff.*,
COUNT(t.id)::int AS transceiver_count,
COALESCE(
json_agg(DISTINCT jsonb_build_object(
'id', t.id,
'part_number', t.part_number,
'speed_gbps', t.speed_gbps,
'fiber_type', t.fiber_type,
'reach_label', t.reach_label
)) FILTER (WHERE t.id IS NOT NULL),
'[]'
) AS sample_transceivers
FROM form_factors ff
LEFT JOIN transceivers t ON t.form_factor = ff.name
WHERE LOWER(ff.name) = LOWER($1)
GROUP BY ff.id
`, [req.params.name]);
if (!rows.length) {
return res.status(404).json({ success: false, error: "Form factor not found" });
}
res.json({ success: true, data: rows[0] });
} catch (err) {
console.error(`GET /api/form-factors/${req.params.name} error:`, err);
res.status(500).json({ success: false, error: "Internal server error" });
}
});