// Vrai-Prix — méthode du coût : fiche d'un assemblage (/cout/assemblage/[code]) — description, schéma, composition, // prix + provenance, main-d'œuvre, sources, historique 8 trimestres, variations régionales, voisins. "use client"; import { useState } from "react"; import Link from "next/link"; import { useLang } from "@/components/LangContext"; import type { Assembly, AssemblyUnitCost, CostLocation } from "@/lib/cost/types"; import { categoryLabel, MASTERFORMAT, QUALITIES, tradeLabel } from "@/lib/cost/taxonomy"; import { UNIT_LABELS } from "@/lib/cost/units"; import { num } from "@/components/avendre/fmt"; import { AssemblySketch, StackBar, UnitCostHistory } from "./Charts"; import { fmtDate, KindIcon, KindLegend, kindLabel, LayerBadge } from "./Provenance"; import { Drawer, PriceSourceCard } from "./SourceDrawer"; export interface AssemblyDetailData { asOf: string; location: CostLocation; assembly: Assembly; unit: AssemblyUnitCost; regional: { code: string; nameFr: string; nameEn: string; direct: number; materialFactor: number; labourFactor: number; equipmentFactor: number }[]; history: { date: string; direct: number; material: number; labour: number; equipment: number; kind: string }[]; neighbours: { code: string; nameFr: string; nameEn: string; unit: string; direct: number }[]; } export default function AssemblyDetail({ data }: { data: AssemblyDetailData }) { const { lang } = useLang(); const fr = lang === "fr"; const a = data.assembly; const u = data.unit; const unitL = (x: string) => UNIT_LABELS[x as keyof typeof UNIT_LABELS]?.[fr ? "fr" : "en"] ?? x; const [src, setSrc] = useState(null); const maxRegional = Math.max(...data.regional.map((r) => r.direct), 0.01); const observedShare = Math.round(u.observedShare * 100); return (
{fr ? "prix au" : "prices as of"} {data.asOf} · {fr ? data.location.nameFr : data.location.nameEn} · v{a.version}
{categoryLabel(a.category, fr)} · MasterFormat {a.masterformat} — {MASTERFORMAT[a.masterformat]?.[fr ? "fr" : "en"] ?? ""}

{fr ? a.nameFr : a.nameEn}

{fr ? a.descriptionFr : a.descriptionEn}

{fr ? "Unité" : "Unit"}

{unitL(a.unit)}

{fr ? "Qualité" : "Quality"}

{a.quality ? (QUALITIES.find((q) => q.key === a.quality)?.[fr ? "fr" : "en"] ?? a.quality) : (fr ? "toutes" : "all")}

{fr ? "Vie économique" : "Economic life"}

{a.economicLife ?? "—"} {fr ? "ans" : "yrs"}

{fr ? "Groupe de condition" : "Condition group"}

{a.conditionGroup ?? "—"}

{fr ? "Mise à jour des prix" : "Price update"}

{u.freshestObservation ? fmtDate(u.freshestObservation, fr) : (fr ? "référence seulement" : "reference only")}

01 {fr ? "Composition" : "Composition"}

{fr ? "Composants, quantités, prix et main-d'œuvre" : "Components, quantities, prices and labour"}

{fr ? "Par unité d'assemblage : matériau = quantité × (1 + pertes) × prix canonique ; main-d'œuvre = heures × coût employeur du métier ; équipement = forfait par unité. Cliquez une ligne pour la fiche source." : "Per assembly unit: material = quantity × (1 + waste) × canonical price; labour = hours × trade employer cost; equipment = lump per unit. Click a row for the source card."}

{u.components.map((c, i) => ( setSrc(c)}> ))}
{fr ? "Composant" : "Component"}{fr ? "Qté / unité" : "Qty / unit"}{fr ? "Pertes" : "Waste"}{fr ? "Prix unitaire" : "Unit price"}{fr ? "Provenance" : "Provenance"}{fr ? "Matériau" : "Material"}{fr ? "Heures" : "Hours"}{fr ? "Taux" : "Rate"}MOÉ
{fr ? c.nameFr : c.nameEn}{c.itemCode && <>
{c.itemCode}}{c.trade && c.labourHours > 0 && <>
{tradeLabel(c.trade, fr)}}
{c.itemCode ? `${num(c.quantity, lang, 4)} ${unitL(c.unit)}` : "—"} {c.itemCode ? `${Math.round(c.wasteFactor * 100)} %` : "—"} {c.itemCode ? `${num(c.unitPrice, lang, 2)} $` : "—"} {c.itemCode ? {kindLabel(c.provenance.kind, fr)}{c.provenance.observedAt ? ` · ${c.provenance.observedAt}` : ""} : } {c.materialCost ? `${num(c.materialCost, lang, 2)} $` : "—"} {c.labourHours ? num(c.labourHours, lang, 3) : "—"} {c.hourlyRate != null ? {num(c.hourlyRate, lang, 2)} $/h : "—"} {c.labourCost ? `${num(c.labourCost, lang, 2)} $` : "—"} {c.equipmentCost ? `${num(c.equipmentCost, lang, 2)} $` : "—"}
{fr ? "Total par unité" : "Total per unit"}{num(u.material, lang, 2)} ${num(u.components.reduce((s, c) => s + c.labourHours, 0), lang, 3)} h{num(u.labour, lang, 2)} ${num(u.equipment, lang, 2)} $
02 {fr ? "Historique" : "History"}

{fr ? "Coût unitaire — 8 trimestres" : "Unit cost — 8 quarters"}

{fr ? "Recalculé avec les prix disponibles à chaque date (observé ●, indexé ○ ou référence ◇). Une base de référence stable donne une ligne plate : ce n'est pas une observation." : "Recomputed with the prices available at each date (observed ●, indexed ○ or reference ◇). A stable reference base gives a flat line: it is not an observation."}

03 {fr ? "Régions" : "Regions"}

{fr ? "Variations régionales" : "Regional variations"}

{fr ? "Direct × facteurs matériaux / main-d'œuvre / équipement de chaque région (conventions CCQ uniformes → main-d'œuvre 1,00 hors régions éloignées)." : "Direct × material / labour / equipment factors of each region (uniform CCQ agreements → labour 1.00 outside remote regions)."}

{data.regional.map((r) => (
{fr ? r.nameFr : r.nameEn} {num(r.direct, lang, 2)} $
))}
04 {fr ? "Voisins" : "Neighbours"}

{fr ? "Assemblages de la même catégorie" : "Assemblies of the same category"}

{data.neighbours.map((n) => (

{n.code}

{fr ? n.nameFr : n.nameEn}

{num(n.direct, lang, 2)} $ / {unitL(n.unit)}

))}
{fr ? "Tout le catalogue" : "Full catalogue"} →
setSrc(null)} title={fr ? "Source" : "Source"} fr={fr}> {src && (
{src.itemCode ? : null} {src.rateProvenance && (

{fr ? "Taux de main-d'œuvre" : "Labour rate"}

{tradeLabel(src.trade, fr)} — {num(src.hourlyRate ?? 0, lang, 2)} $/h

{kindLabel(src.rateProvenance.kind, fr)} · {src.rateProvenance.source}{src.rateProvenance.effectiveDate ? ` · ${src.rateProvenance.effectiveDate}` : ""}

{src.rateProvenance.note &&

{src.rateProvenance.note}

} {src.rateProvenance.sourceUrl && {fr ? "Document source" : "Source document"} ↗}
)} {!src.itemCode && !src.rateProvenance &&

{fr ? "Équipement : forfait par unité d'assemblage (hypothèse documentée)." : "Equipment: lump per assembly unit (documented assumption)."}{src.provenance.note ? ` ${src.provenance.note}` : ""}

}
)}
); }