- 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
74 lines
2.4 KiB
TypeScript
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" });
|
|
}
|
|
});
|