Repository Public
1// Vrai-Prix — méthode du coût : catalogue des assemblages (+ fiche d'un article via ?item=CODE).2import type { Metadata } from "next";3import AssemblyCatalog, { type CatalogRow, type ItemCard } from "@/components/cout/AssemblyCatalog";4import { buildContext, getItem, priceHistory } from "@/lib/cost/catalog";5import { assemblyUnitCost } from "@/lib/cost/engine";6import { cached } from "@/lib/cost/cache";78export const dynamic = "force-dynamic";910export const metadata: Metadata = {11 title: "Catalogue des assemblages de construction — méthode du coût",12 description: "Plus de 70 assemblages résidentiels (fondations, structure, enveloppe, toiture, ouvertures, finitions, cuisine, salle de bain, mécanique, électricité, extérieur) décomposés en matériaux, heures de main-d'œuvre APCHQ/CCQ et équipement, avec provenance de chaque prix.",13 alternates: { canonical: "/cout/assemblage" },14};1516export default async function AssemblyCatalogPage({ searchParams }: { searchParams: Promise<Record<string, string | string[] | undefined>> }) {17 const sp = await searchParams;18 const itemCode = typeof sp.item === "string" ? sp.item.slice(0, 40) : null;19 let rows: CatalogRow[] = [];20 let asOf = "";21 let item: ItemCard | null = null;22 try {23 const data = cached("catalog:QC-MTL", 10 * 60_000, () => {24 const ctx = buildContext({ locationCode: "QC-MTL" });25 return { asOf: ctx.asOf, rows: [...ctx.assemblies.values()].map((a) => { const u = assemblyUnitCost(a, ctx); return { code: a.code, nameFr: a.nameFr, nameEn: a.nameEn, unit: a.unit, category: a.category, quality: a.quality, economicLife: a.economicLife, material: u.material, labour: u.labour, equipment: u.equipment, direct: u.direct, observedShare: u.observedShare, freshestObservation: u.freshestObservation, components: a.components.length } satisfies CatalogRow; }), ctx };26 });27 rows = data.rows; asOf = data.asOf;28 if (itemCode) {29 const it = getItem(itemCode);30 if (it) item = { item: it, price: data.ctx.prices.get(it.code) ?? null, history: priceHistory(it.code).map((h) => ({ date: h.date, price: h.price, source: h.source, url: h.url, regular: h.regular, kind: h.kind, outlier: h.outlier })), usedBy: [...data.ctx.assemblies.values()].filter((a) => a.components.some((c) => c.itemCode === it.code)).map((a) => ({ code: a.code, nameFr: a.nameFr, nameEn: a.nameEn, unit: a.unit, quantity: a.components.find((c) => c.itemCode === it.code)!.quantity })) };31 }32 } catch { /* base indisponible */ }33 return <AssemblyCatalog rows={rows} asOf={asOf} item={item} />;34}35