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