import { Router, Request, Response } from "express"; import { pool } from "../db/client"; export const equivalencesRouter = Router(); // GET /api/equivalences?q=&vendor=&limit=50&offset=0 // Search equivalences by competitor or Flexoptix part number equivalencesRouter.get("/", async (req: Request, res: Response) => { const { q, vendor, limit: lim, offset: off } = req.query as Record; const limit = Math.min(parseInt(lim || "50"), 200); const offset = parseInt(off || "0"); const conditions: string[] = ["e.status IN ('approved', 'auto_approved')"]; const values: unknown[] = []; let idx = 1; if (q) { conditions.push( `(fx.part_number ILIKE $${idx} OR fx.standard_name ILIKE $${idx} OR cx.part_number ILIKE $${idx} OR cx.standard_name ILIKE $${idx})` ); values.push(`%${q}%`); idx++; } if (vendor) { conditions.push(`(cv.name ILIKE $${idx} OR fv.name ILIKE $${idx})`); values.push(`%${vendor}%`); idx++; } const where = `WHERE ${conditions.join(" AND ")}`; const query = ` SELECT e.id, e.confidence, e.match_basis, e.status, e.created_at, -- Flexoptix side fx.id AS flexoptix_id, fx.part_number AS flexoptix_pn, fx.standard_name AS flexoptix_std, fx.form_factor AS flexoptix_form_factor, fx.speed AS flexoptix_speed, fx.speed_gbps AS flexoptix_speed_gbps, fx.reach_label AS flexoptix_reach, fx.product_page_url AS flexoptix_url, fx.price_verified_eur AS flexoptix_price_eur, fx.market_status AS flexoptix_market_status, -- Competitor side cx.id AS competitor_id, cx.part_number AS competitor_pn, cx.standard_name AS competitor_std, cx.form_factor AS competitor_form_factor, cx.speed AS competitor_speed, cx.reach_label AS competitor_reach, cx.product_page_url AS competitor_url, cx.price_verified_eur AS competitor_price_eur, cx.market_status AS competitor_market_status, cv.name AS competitor_vendor, cv.website AS competitor_vendor_website FROM transceiver_equivalences e JOIN transceivers fx ON fx.id = e.flexoptix_id JOIN vendors fv ON fv.id = fx.vendor_id JOIN transceivers cx ON cx.id = e.competitor_id JOIN vendors cv ON cv.id = cx.vendor_id ${where} ORDER BY e.confidence DESC, e.status DESC LIMIT ${limit} OFFSET ${offset} `; const countQuery = ` SELECT COUNT(*) FROM transceiver_equivalences e JOIN transceivers fx ON fx.id = e.flexoptix_id JOIN vendors fv ON fv.id = fx.vendor_id JOIN transceivers cx ON cx.id = e.competitor_id JOIN vendors cv ON cv.id = cx.vendor_id ${where} `; try { const [data, count] = await Promise.all([ pool.query(query, values), pool.query(countQuery, values), ]); res.json({ success: true, data: data.rows, total: parseInt(count.rows[0].count), limit, offset, }); } catch (err) { res.status(500).json({ success: false, error: (err as Error).message }); } }); // GET /api/equivalences/transceiver/:id — all equivalences for a specific transceiver (both sides) equivalencesRouter.get("/transceiver/:id", async (req: Request, res: Response) => { const { id } = req.params; try { const result = await pool.query( `SELECT e.id, e.confidence, e.match_basis, e.status, -- Flexoptix side fx.id AS flexoptix_id, fx.part_number AS flexoptix_pn, fx.standard_name AS flexoptix_std, fx.form_factor AS flexoptix_form_factor, fx.speed AS flexoptix_speed, fx.reach_label AS flexoptix_reach, fx.product_page_url AS flexoptix_url, fx.price_verified_eur AS flexoptix_price_eur, -- Competitor side cx.id AS competitor_id, cx.part_number AS competitor_pn, cx.standard_name AS competitor_std, cx.form_factor AS competitor_form_factor, cx.speed AS competitor_speed, cx.reach_label AS competitor_reach, cx.product_page_url AS competitor_url, cx.price_verified_eur AS competitor_price_eur, cv.name AS competitor_vendor, cv.website AS competitor_vendor_website FROM transceiver_equivalences e JOIN transceivers fx ON fx.id = e.flexoptix_id JOIN transceivers cx ON cx.id = e.competitor_id JOIN vendors cv ON cv.id = cx.vendor_id WHERE (e.flexoptix_id = $1::uuid OR e.competitor_id = $1::uuid) AND e.status IN ('approved', 'auto_approved') ORDER BY e.confidence DESC`, [id] ); res.json({ success: true, data: result.rows, total: result.rows.length }); } catch (err) { res.status(500).json({ success: false, error: (err as Error).message }); } }); // GET /api/equivalences/stats — overview numbers equivalencesRouter.get("/stats", async (_req: Request, res: Response) => { try { const result = await pool.query(` SELECT COUNT(*) FILTER (WHERE status IN ('approved','auto_approved')) AS active, COUNT(DISTINCT competitor_id) FILTER (WHERE status IN ('approved','auto_approved')) AS unique_competitor_products, COUNT(DISTINCT flexoptix_id) FILTER (WHERE status IN ('approved','auto_approved')) AS unique_flexoptix_products, COUNT(DISTINCT cv.name) AS unique_competitor_vendors, AVG(confidence) FILTER (WHERE status IN ('approved','auto_approved'))::numeric(4,3) AS avg_confidence FROM transceiver_equivalences e JOIN transceivers cx ON cx.id = e.competitor_id JOIN vendors cv ON cv.id = cx.vendor_id `); res.json({ success: true, stats: result.rows[0] }); } catch (err) { res.status(500).json({ success: false, error: (err as Error).message }); } }); // GET /api/equivalences/top-vendors — which competitor vendors have most equivalences equivalencesRouter.get("/top-vendors", async (_req: Request, res: Response) => { try { const result = await pool.query(` SELECT cv.name AS vendor, cv.website, COUNT(*) AS equiv_count, COUNT(DISTINCT e.competitor_id) AS products_covered, AVG(e.confidence)::numeric(4,3) AS avg_confidence FROM transceiver_equivalences e JOIN transceivers cx ON cx.id = e.competitor_id JOIN vendors cv ON cv.id = cx.vendor_id WHERE e.status IN ('approved','auto_approved') GROUP BY cv.name, cv.website ORDER BY equiv_count DESC LIMIT 20 `); res.json({ success: true, data: result.rows }); } catch (err) { res.status(500).json({ success: false, error: (err as Error).message }); } });