From 0d6abeb287fb856d3f6d58ed376ec6370e5bef0f Mon Sep 17 00:00:00 2001 From: Aewing Date: Sun, 7 Dec 2025 22:41:33 -0600 Subject: [PATCH] do what we can --- minimal-uploader-playground/run.js | 47 ++++++++++------- minimal-uploader-playground/server.js | 4 +- .../minimal-playground/public/taskpane.js | 50 ++++++++++++++++--- 3 files changed, 75 insertions(+), 26 deletions(-) diff --git a/minimal-uploader-playground/run.js b/minimal-uploader-playground/run.js index 484c998..d3bba01 100644 --- a/minimal-uploader-playground/run.js +++ b/minimal-uploader-playground/run.js @@ -507,7 +507,7 @@ async function createShareLink(accessToken, driveId, itemId) { return json; } -async function run() { +async function run(incomingRecords) { console.log('minimal-uploader starting', dryRun ? '(dry-run)' : ''); const token = await getAppToken(); console.log('acquired app token (truncated):', token ? token.substring(0, 30) + '...' : '(none)'); @@ -522,25 +522,34 @@ async function run() { } const safeMainFolderName = sanitizeName(mainFolderName); const subFolderName = 'Managers Info'; - // Prefer generating from an Excel workbook if EXCEL_DRIVE_ID and EXCEL_ITEM_ID are present - let csvBase64 = await pdfFromCsvToBase64(pathToCsv); - if (process.env.EXCEL_DRIVE_ID && process.env.EXCEL_ITEM_ID) { - console.log('EXCEL_DRIVE_ID/EXCEL_ITEM_ID present, downloading workbook from Graph to build PDF'); - try { - const token1 = token; // app token used for Graph - const workbookBuffer = await downloadExcelFromGraph(token1, process.env.EXCEL_DRIVE_ID, process.env.EXCEL_ITEM_ID); - // Build a PDF directly from the workbook and its formatting/regions if possible - const wbPdf = await pdfFromWorkbookBufferToBase64(workbookBuffer); - if (wbPdf) { - csvBase64 = wbPdf; - // count rows if we can - const recs = parseManagersSheetFromWorkbookBuffer(workbookBuffer) || []; - console.log('Built PDF from workbook Managers Info with', recs.length, 'rows'); - } else { - console.log('No Managers Info sheet / no rows found in workbook — falling back to managers.csv or static PDF'); + + // Priority: use incoming records from add-in, else try CSV, else try workbook from Graph + let csvBase64 = null; + + if (incomingRecords && incomingRecords.length > 0) { + console.log('Using records from Excel add-in:', incomingRecords.length, 'rows'); + csvBase64 = await pdfFromRecordsToBase64(incomingRecords, 'Managers Info'); + } else { + // fallback to CSV file or Graph workbook + csvBase64 = await pdfFromCsvToBase64(pathToCsv); + if (process.env.EXCEL_DRIVE_ID && process.env.EXCEL_ITEM_ID) { + console.log('EXCEL_DRIVE_ID/EXCEL_ITEM_ID present, downloading workbook from Graph to build PDF'); + try { + const token1 = token; // app token used for Graph + const workbookBuffer = await downloadExcelFromGraph(token1, process.env.EXCEL_DRIVE_ID, process.env.EXCEL_ITEM_ID); + // Build a PDF directly from the workbook and its formatting/regions if possible + const wbPdf = await pdfFromWorkbookBufferToBase64(workbookBuffer); + if (wbPdf) { + csvBase64 = wbPdf; + // count rows if we can + const recs = parseManagersSheetFromWorkbookBuffer(workbookBuffer) || []; + console.log('Built PDF from workbook Managers Info with', recs.length, 'rows'); + } else { + console.log('No Managers Info sheet / no rows found in workbook — falling back to managers.csv or static PDF'); + } + } catch (e) { + console.error('Failed to build PDF from workbook', e && e.message ? e.message : e); } - } catch (e) { - console.error('Failed to build PDF from workbook', e && e.message ? e.message : e); } } const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); diff --git a/minimal-uploader-playground/server.js b/minimal-uploader-playground/server.js index d33e7c2..a96b7b1 100644 --- a/minimal-uploader-playground/server.js +++ b/minimal-uploader-playground/server.js @@ -19,7 +19,9 @@ app.get('/api/health', (req, res) => res.json({ ok: true, ts: new Date().toISOSt // main endpoint — run the uploader flow (download workbook if env vars exist, create folders and upload PDF) app.post('/api/upload-managers', async (req, res) => { try { - const result = await run(); + // Accept records from request body (sent from Excel add-in) + const records = req.body && req.body.records ? req.body.records : null; + const result = await run(records); res.json({ ok: true, result }); } catch (err) { console.error('Error in /api/upload-managers', err); diff --git a/officeaddin/minimal-playground/public/taskpane.js b/officeaddin/minimal-playground/public/taskpane.js index acc7e38..a324994 100644 --- a/officeaddin/minimal-playground/public/taskpane.js +++ b/officeaddin/minimal-playground/public/taskpane.js @@ -1,7 +1,5 @@ -// Minimal add-in taskpane script — one button that calls the playground server -// Wait for Office to be ready before attaching event handlers +// Minimal add-in taskpane script — reads Excel data and uploads to playground server Office.onReady(function(info) { - // info.host will be 'Excel' when running in Excel console.log('Office.onReady fired, host:', info.host); const uploadBtn = document.getElementById('uploadBtn'); @@ -11,15 +9,55 @@ Office.onReady(function(info) { status.innerHTML = '
' + text + '
'; } + // Read data from worksheet "Managers Info" and convert A1:E49 into array of objects + // Only rows with any non-empty cell are included; first row is header + async function getExcelData() { + return Excel.run(async (context) => { + // Use the specific worksheet by name and the fixed range A1:E49 + const sheet = context.workbook.worksheets.getItem('Managers Info'); + const range = sheet.getRange('a2:E49'); + range.load('values'); + await context.sync(); + + const values = range.values; + if (!values || values.length < 2) { + throw new Error('No data found in Managers Info A1:E49 (need header row + at least one data row)'); + } + + const headers = values[0].map(h => String(h || '').trim()); + const records = []; + for (let i = 1; i < values.length; i++) { + const row = values[i]; + // skip fully-empty rows + const isEmpty = row.every(c => c === null || c === undefined || String(c).trim() === ''); + if (isEmpty) continue; + const obj = {}; + headers.forEach((h, idx) => { + obj[h] = row[idx]; + }); + records.push(obj); + } + if (records.length === 0) throw new Error('No data rows found in Managers Info A1:E49'); + return records; + }); + } + uploadBtn.addEventListener('click', async function() { uploadBtn.disabled = true; - setStatus('Starting upload...'); + setStatus('Reading Excel data...'); try { - const res = await fetch('http://localhost:3002/api/upload-managers', { method: 'POST' }); + const records = await getExcelData(); + setStatus('Uploading ' + records.length + ' rows...'); + + const res = await fetch('http://localhost:3002/api/upload-managers', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ records: records }) + }); const json = await res.json(); if (!res.ok) throw new Error(json && json.error ? json.error : 'Unknown response'); var link = (json.result && json.result.share && json.result.share.link) ? json.result.share.link.webUrl : 'unknown'; - setStatus('Upload finished. Share link: ' + link, true); + setStatus('Upload finished. Share link: ' + link + '', true); } catch (err) { console.error(err); setStatus('Upload failed: ' + (err.message || err), false);