From 53aa23f762e0854a1b8d030bd7e45b9d7a5e758e Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 19 Feb 2026 17:26:43 -0500 Subject: [PATCH] refactor: consolidate export detail into drawing detail page Remove the duplicate export detail page and route exports list directly to drawing detail. When navigating from exports, the specific export's BOM items are shown via eid param; from drawings, items are deduplicated to the latest revision. Add Rev column, PDF download, and All DXFs download to drawing detail. Co-Authored-By: Claude Opus 4.6 --- FabWorks.Api/wwwroot/js/pages.js | 140 ++++++++++-------------------- FabWorks.Api/wwwroot/js/router.js | 7 +- 2 files changed, 52 insertions(+), 95 deletions(-) diff --git a/FabWorks.Api/wwwroot/js/pages.js b/FabWorks.Api/wwwroot/js/pages.js index c89b136..5ad16c8 100644 --- a/FabWorks.Api/wwwroot/js/pages.js +++ b/FabWorks.Api/wwwroot/js/pages.js @@ -32,7 +32,7 @@ const pages = { setPage('Exports', `${data.items.length} exports`); const rows = data.items.map((e, i) => ` - + ${e.id} ${esc(e.drawingNumber) || '\u2014'} ${esc(e.title) || ''} @@ -62,85 +62,6 @@ const pages = { } }, - async exportDetail(id) { - const actions = document.getElementById('topbar-actions'); - const content = document.getElementById('page-content'); - setPage('Loading...'); - actions.innerHTML = ''; - content.innerHTML = `
Loading export
`; - - try { - const exp = await api.get(`/api/exports/${id}`); - setPage(exp.drawingNumber || `Export #${exp.id}`, 'export detail'); - - const dxfCount = (exp.bomItems || []).filter(b => b.cutTemplate?.contentHash).length; - - const bomRows = (exp.bomItems || []).map((b, i) => { - const hasDetails = b.cutTemplate || b.formProgram; - const toggleId = `bom-${b.id}`; - return ` - - ${hasDetails ? `${icons.chevron}` : ''} - ${esc(b.itemNo)} - ${esc(b.partName)} - ${esc(b.description)} - ${esc(b.material)} - ${b.qty ?? ''} - ${b.totalQty ?? ''} - - ${b.cutTemplate ? `${icons.laser} DXF` : ''} - ${b.formProgram ? `${icons.bend} Form` : ''} - - - ${hasDetails ? `${renderBomDetails(b)}` : ''}`; - }).join(''); - - content.innerHTML = ` - ${icons.back} Back to exports - -
-
Export Information
-
-
-
${esc(exp.drawingNumber) || '\u2014'}
- ${exp.title ? `
${esc(exp.title)}
` : ''} -
${esc(exp.exportedBy)}
-
${fmtDate(exp.exportedAt)}
-
${esc(exp.sourceFilePath)}
-
-
-
- -
-
- BOM Items - ${exp.bomItems?.length || 0} items - - ${exp.pdfContentHash ? `${icons.download} PDF` : ''} - ${dxfCount > 0 ? `${icons.download} All DXFs` : ''} - - -
- ${exp.bomItems?.length ? ` - - - - - - - - - - - - ${bomRows} -
ItemPart NameDescriptionMaterialQtyTotalData
` : '
No BOM items for this export.
'} -
`; - } catch (err) { - content.innerHTML = `
Error: ${esc(err.message)}
`; - } - }, - async drawings(params) { const actions = document.getElementById('topbar-actions'); const content = document.getElementById('page-content'); @@ -255,8 +176,9 @@ const pages = { } }, - async drawingDetail(drawingEncoded) { + async drawingDetail(drawingEncoded, params) { const drawingNumber = decodeURIComponent(drawingEncoded); + const exportId = params?.eid ? parseInt(params.eid) : null; const actions = document.getElementById('topbar-actions'); const content = document.getElementById('page-content'); setPage(drawingNumber, 'drawing'); @@ -273,12 +195,23 @@ const pages = { return; } - const allBom = []; - exports.forEach(exp => { - (exp.bomItems || []).forEach(b => { - allBom.push({ ...b, exportId: exp.id, exportedAt: exp.exportedAt }); + let allBom; + const singleExport = exportId ? exports.find(e => e.id === exportId) : null; + if (singleExport) { + // Viewing a specific export - show only its BOM items + allBom = (singleExport.bomItems || []).map(b => ({ ...b, exportId: singleExport.id, exportedAt: singleExport.exportedAt })); + } else { + // Viewing drawing overview - deduplicate by itemNo, keeping latest revision (exports are newest-first) + const bomByItem = new Map(); + exports.forEach(exp => { + (exp.bomItems || []).forEach(b => { + if (!bomByItem.has(b.itemNo)) { + bomByItem.set(b.itemNo, { ...b, exportId: exp.id, exportedAt: exp.exportedAt }); + } + }); }); - }); + allBom = [...bomByItem.values()]; + } const bomRows = allBom.map((b, i) => { const hasDetails = b.cutTemplate || b.formProgram; @@ -288,6 +221,7 @@ const pages = { ${hasDetails ? `${icons.chevron}` : ''} ${esc(b.itemNo)} ${esc(b.partName)} + ${b.cutTemplate?.revision ?? ''} ${esc(b.description)} ${esc(b.material)} ${b.qty ?? ''} @@ -297,22 +231,43 @@ const pages = { ${b.formProgram ? `${icons.bend} Form` : ''} - ${hasDetails ? `${renderBomDetails(b)}` : ''}`; + ${hasDetails ? `${renderBomDetails(b)}` : ''}`; }).join(''); - content.innerHTML = ` - ${icons.back} Back to drawings + const backLink = singleExport + ? `${icons.back} Back to exports` + : `${icons.back} Back to drawings`; -
+ const statsHtml = singleExport + ? `
+
Exported By
${esc(singleExport.exportedBy)}
+
BOM Items
${allBom.length}
+
Exported
${fmtDate(singleExport.exportedAt)}
+
` + : `
Exports
${exports.length}
BOM Items
${allBom.length}
Latest Export
${fmtDate(exports[0].exportedAt)}
-
+
`; + + const bomHeader = singleExport ? 'BOM Items' : 'All BOM Items'; + const activeExport = singleExport || exports[0]; + const dxfCount = allBom.filter(b => b.cutTemplate?.contentHash).length; + const pdfHash = activeExport.pdfContentHash; + const pdfName = encodeURIComponent((drawingNumber || 'drawing') + '.pdf'); + + content.innerHTML = ` + ${backLink} + ${statsHtml}
- All BOM Items + ${bomHeader} ${allBom.length} items + + ${pdfHash ? `${icons.download} PDF` : ''} + ${dxfCount > 0 ? `${icons.download} All DXFs` : ''} +
${allBom.length ? ` @@ -320,6 +275,7 @@ const pages = { + diff --git a/FabWorks.Api/wwwroot/js/router.js b/FabWorks.Api/wwwroot/js/router.js index 66fdfc1..a767652 100644 --- a/FabWorks.Api/wwwroot/js/router.js +++ b/FabWorks.Api/wwwroot/js/router.js @@ -1,6 +1,9 @@ const router = { go(page, params = {}) { - const hash = page + (params.id ? '/' + params.id : '') + (params.q ? '?q=' + encodeURIComponent(params.q) : ''); + const qParts = []; + if (params.q) qParts.push('q=' + encodeURIComponent(params.q)); + if (params.eid) qParts.push('eid=' + encodeURIComponent(params.eid)); + const hash = page + (params.id ? '/' + params.id : '') + (qParts.length ? '?' + qParts.join('&') : ''); location.hash = hash; }, parse() { @@ -20,12 +23,10 @@ const router = { document.querySelectorAll('.nav-item').forEach(el => { el.classList.toggle('active', el.dataset.page === page || - (page === 'export-detail' && el.dataset.page === 'exports') || (page === 'drawing-detail' && el.dataset.page === 'drawings')); }); switch(page) { case 'exports': pages.exports(params); break; - case 'export-detail': pages.exportDetail(id); break; case 'drawings': pages.drawings(params); break; case 'drawing-detail': pages.drawingDetail(id, params); break; case 'files': pages.files(params); break;
Item Part NameRev Description Material Qty