diff --git a/.gitignore b/.gitignore index b90c007..dc47ccf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ # Ignore all .xlsx files -*.xlsx \ No newline at end of file +# *.xlsx \ No newline at end of file diff --git a/data/law_dept_sample/OT.json b/data/law_dept_sample/OT.json deleted file mode 100644 index 8d6aeae..0000000 --- a/data/law_dept_sample/OT.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "Account String": "1000-29320-320010", - "Cost Center" : "Law Administration", - "Overtime Wages": 15000, - "Overtime Salary": 15000 - }, - { - "Account String": "1000-29320-320010", - "Cost Center" : "Law Department Grants", - "Overtime Wages": 15000, - "Overtime Salary": 0 - } -] \ No newline at end of file diff --git a/data/law_dept_sample/funds.json b/data/law_dept_sample/funds.json deleted file mode 100644 index 03df948..0000000 --- a/data/law_dept_sample/funds.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { "ID" : "1000", - "Name" : "General Fund"}, - { "ID" : "29320", - "Name" : "Sample Grant Fund"} -] \ No newline at end of file diff --git a/data/law_dept_sample/nonpersonnel_data.json b/data/law_dept_sample/nonpersonnel_data.json deleted file mode 100644 index 0b8493a..0000000 --- a/data/law_dept_sample/nonpersonnel_data.json +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "Vendor": "Law Firm LLC", - "CPA #" : "765421", - "Account String": "1000-29320-320010", - "Object Name": "Consulting", - "End of Contract": "12/31/2024", - "Amount Remaining" : 50000, - "FY26 Request": 100000 - }, - { - "Vendor": "Office Supplier", - "CPA #" : "1234567", - "Account String": "1000-29320-320010", - "Object Name": "Office Supplies", - "End of Contract": "12/31/2026", - "Amount Remaining" : 500000, - "FY26 Request": 10000 - }, - { - "Vendor": "Software Co.", - "CPA #" : "9876543", - "Account String": "1000-29320-320010", - "Object Name": "Information Technology", - "End of Contract": "6/1/2025", - "Amount Remaining" : 30000, - "FY26 Request": 30000 - } -] \ No newline at end of file diff --git a/data/law_dept_sample/personnel_data.json b/data/law_dept_sample/personnel_data.json deleted file mode 100644 index 27d97c9..0000000 --- a/data/law_dept_sample/personnel_data.json +++ /dev/null @@ -1,23 +0,0 @@ -[ - { - "Job Name (Type)": "Deputy Counsel (Regular)", - "Account String": "1000-29320-320010", - "Service" : "Appeals", - "FY26 FTEs": 1, - "Average Projected Salary": "150000" - }, - { - "Job Name (Type)": "Legal Secretary (Regular)", - "Account String": "1000-29320-320010", - "Service" : "FOIA", - "FY26 FTEs": 2, - "Average Projected Salary": "55000" - }, - { - "Job Name (Type)": "Assistant Counsel (Regular)", - "Account String": "1000-29320-320010", - "Service" : "Appeals", - "FY26 FTEs": 10, - "Average Projected Salary": "80000" - } -] \ No newline at end of file diff --git a/data/law_dept_sample/services.json b/data/law_dept_sample/services.json deleted file mode 100644 index 2a4dd15..0000000 --- a/data/law_dept_sample/services.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { "id" : "", - "name" : "Select"}, - { "id" : "Appeals", - "name" : "Appeals"}, - { "id" : "FOIA", - "name" : "FOIA" }, - { "id" : "Lobbying", - "name" : "Lobbying"} -] \ No newline at end of file diff --git a/data/law_dept_sample/strings.json b/data/law_dept_sample/strings.json deleted file mode 100644 index 955a422..0000000 --- a/data/law_dept_sample/strings.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "1000" : { - "label" : "General Fund", - "appropriations" : { - "29320" : { - "label" : "Efficient and Innovative Operations Support", - "cost centers" : { - "320010" : { "label" : "Law Administration" }, - "321111" : { "label" : "Law Department Grants" } - } - } - } - }, - - "2119" : { - "label" : "FY2020 MIDC Grant", - "appropriations" : { - "21206" : { - "label" : "2023 Michigan Indigent Defense Commission", - "cost centers" : { - "320010" : { "label" : "Law Administration" }, - "321111" : { "label" : "Law Department Grants" } - } - } - } - }, - - "2490" : { - "label" : "Construction Code Fund", - "appropriations" : { - "25130" : { - "label" : "BSEED Safe Buildings", - "cost centers" : { - "320010" : { "label" : "Law Administration" } - } - } - } - } -} \ No newline at end of file diff --git a/index.html b/index.html index 4d29da1..643a389 100644 --- a/index.html +++ b/index.html @@ -22,6 +22,7 @@ + @@ -75,6 +76,9 @@

Baseline


+ + +
diff --git a/js/components/body/body.js b/js/components/body/body.js index a317e91..fff8b26 100644 --- a/js/components/body/body.js +++ b/js/components/body/body.js @@ -1,5 +1,6 @@ import Welcome from '../../components/welcome/welcome.js' import { Accordion } from '../accordion/accordion.js'; +import { FileUpload } from '../file_upload/file_upload.js'; import Modal from '../modal/modal.js'; import NavButtons from '../nav_buttons/nav_buttons.js'; import Prompt from '../prompt/prompt.js'; @@ -16,6 +17,7 @@ function resetPage() { Table.hide(); Sidebar.hide(); Accordion.hide(); + FileUpload.hide(); // disable next button NavButtons.Next.disable(); Prompt.Buttons.reset(); diff --git a/js/components/file_upload/file_upload.css b/js/components/file_upload/file_upload.css new file mode 100644 index 0000000..c2e0a7e --- /dev/null +++ b/js/components/file_upload/file_upload.css @@ -0,0 +1,3 @@ +#file-input { + margin-left: 40%; +} \ No newline at end of file diff --git a/js/components/file_upload/file_upload.js b/js/components/file_upload/file_upload.js new file mode 100644 index 0000000..df3a647 --- /dev/null +++ b/js/components/file_upload/file_upload.js @@ -0,0 +1,32 @@ +import { processWorkbook } from "../../utils/data_utils/XLSX_handlers.js"; + +export const FileUpload = { + init : function() { + const inputObject = document.getElementById('file-input'); + inputObject.addEventListener('change', function(event) {readXL(event) }); + }, + show : function(){ + const inputObject = document.getElementById('file-input'); + inputObject.style.display = ''; + }, + hide : function(){ + const inputObject = document.getElementById('file-input'); + inputObject.style.display = 'none'; + } +} + +function readXL(event) { + const file = event.target.files[0]; + + if (file) { + const reader = new FileReader(); + reader.onload = function(e) { + const arrayBuffer = e.target.result; + processWorkbook(arrayBuffer); + }; + reader.onerror = function(err) { + console.error('Error reading file:', err); + }; + reader.readAsArrayBuffer(file); // Read the file as an ArrayBuffer + } +} \ No newline at end of file diff --git a/js/components/table/subcomponents/cells.js b/js/components/table/subcomponents/cells.js index 122c1be..fe061d7 100644 --- a/js/components/table/subcomponents/cells.js +++ b/js/components/table/subcomponents/cells.js @@ -29,7 +29,6 @@ function createEditableCell(cellClass, isCost){ textbox.type = 'text'; if (isCost){ var value = cell.getAttribute('value'); - console.log(value); } else { var value = cell.textContent; } diff --git a/js/components/table/subcomponents/data.js b/js/components/table/subcomponents/data.js index 1846d0d..9342721 100644 --- a/js/components/table/subcomponents/data.js +++ b/js/components/table/subcomponents/data.js @@ -3,38 +3,33 @@ import { CurrentFund, CurrentPage, loadTableData, saveTableData } from "../../.. function fillTable(data) { try { - if(Array.isArray(data)) { - const table = document.getElementById('main-table'); - const thead = table.querySelector('thead'); - const tbody = table.querySelector('tbody'); - - // clear existing data - thead.innerHTML = ''; - tbody.innerHTML = ''; - - // Create table header row - const headerRow = document.createElement('tr'); - Object.keys(data[0]).forEach(key => { - const header = document.createElement('th'); - header.textContent = key; - headerRow.appendChild(header); + const table = document.getElementById('main-table'); + const thead = table.querySelector('thead'); + const tbody = table.querySelector('tbody'); + + // clear existing data + thead.innerHTML = ''; + tbody.innerHTML = ''; + + // Create table header row + const headerRow = document.createElement('tr'); + Object.keys(data[0]).forEach(key => { + const header = document.createElement('th'); + header.textContent = key; + headerRow.appendChild(header); + }); + thead.appendChild(headerRow); + + // Create table body rows + data.forEach(item => { + const row = document.createElement('tr'); + Object.values(item).forEach(val => { + const cell = document.createElement('td'); + cell.textContent = val; + row.appendChild(cell); }); - thead.appendChild(headerRow); - - // Create table body rows - data.forEach(item => { - const row = document.createElement('tr'); - Object.values(item).forEach(val => { - const cell = document.createElement('td'); - cell.textContent = val; - row.appendChild(cell); - }); - tbody.appendChild(row); - }); - - } else { - console.error('Empty table saved in localStorage.'); - } + tbody.appendChild(row); + }); } catch(error) { console.error('No table saved in localStorage:', error); } @@ -42,10 +37,20 @@ function fillTable(data) { } async function loadFromStorage(){ - // look up table in storage and pass to table load function - const key = `${CurrentPage.load()}_${CurrentFund.number()}`; + // look up table in storage and pass to table load function\ + if (CurrentFund.number()){ + var key = `${CurrentPage.load()}_${CurrentFund.number()}`; + } else { + var key = CurrentPage.load(); + } const data = await loadTableData(key); - fillTable(data); + if (!data){ + // if no table in storage, return 0 + return 0; + } else { + fillTable(data); + return 1; + } } diff --git a/js/components/table/table.css b/js/components/table/table.css index eec709a..a8c20ad 100644 --- a/js/components/table/table.css +++ b/js/components/table/table.css @@ -49,7 +49,7 @@ div.table-container { /* max-width: calc(100vw - var(--sidebar-width)); */ /* margin: auto; */ max-height: max(350px, 6vh); - min-height: 350px; + /* min-height: 350px; */ } diff --git a/js/init.js b/js/init.js index d7264c9..50599d0 100644 --- a/js/init.js +++ b/js/init.js @@ -1,5 +1,4 @@ // import functions -import { fetchAndProcessExcel } from './utils/data_utils/XLSX_handlers.js'; import { CurrentPage } from './utils/data_utils/local_storage_handlers.js'; // path for my laptop @@ -8,7 +7,7 @@ export let DATA_ROOT = '../../../data/law_dept_sample/' // export let DATA_ROOT = '../../budget-request-demo/data/law_dept_sample/' export let REVENUE = 0; -export let TARGET = 20000000; +export let TARGET = 14000000; export var FISCAL_YEAR = '26'; export var OT_FRINGE = 0.0765; diff --git a/js/utils/data_utils/XLSX_handlers.js b/js/utils/data_utils/XLSX_handlers.js index 28db656..1bb8dae 100644 --- a/js/utils/data_utils/XLSX_handlers.js +++ b/js/utils/data_utils/XLSX_handlers.js @@ -3,28 +3,7 @@ import { SHEETS } from '../../init.js'; import { FundLookupTable, Services } from './budget_data_handlers.js'; import { removeNewLines } from '../common_utils.js'; - -export async function fetchAndProcessExcel(filePath) { - fetch(filePath) - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.arrayBuffer(); // return the ArrayBuffer - }) - .then(data => { - try { - // Read the data into a workbook - const workbook = XLSX.read(data, { type: 'array' }); - processWorkbook(workbook); - } catch (err) { - console.error('Error reading the Excel file:', err); - } - }) - .catch(error => { - console.error('Error fetching the Excel file:', error); - }); -} +import { Baseline } from './local_storage_handlers.js'; function deleteTopRowsUntilFullData(data) { // function to try to find the top of the usable data @@ -32,7 +11,6 @@ function deleteTopRowsUntilFullData(data) { while (!fullDataRowFound && data.length > 0) { const row = data[0]; // Get the top row - //console.log(row); let hasAllData = true; for (const cell of row) { @@ -53,13 +31,14 @@ function deleteTopRowsUntilFullData(data) { return data; } -function processWorkbook(workbook) { +export function processWorkbook(arrayBuffer) { + const workbook = XLSX.read(arrayBuffer, { type: 'array' }); workbook.SheetNames.forEach(sheetName => { // only convert sheets we need if (Object.keys(SHEETS).includes(sheetName)) { // read in sheets const sheet = workbook.Sheets[sheetName]; - const rawData = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: null }); + const rawData = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: '' }); // Clean the data by removing top rows with incomplete data const dataRows = deleteTopRowsUntilFullData(rawData); @@ -121,4 +100,57 @@ function processWorkbook(workbook) { } } }); + + console.log('all excel data saved'); +} + +// Utility function to append a sheet to the workbook if data is present +function appendSheetToWorkbook(workbook, data, sheetName) { + if (data.length > 0) { + const sheet = XLSX.utils.json_to_sheet(data); + XLSX.utils.book_append_sheet(workbook, sheet, sheetName); + } } + +export function downloadXLSX() { + const baseline = new Baseline(); + const workbook = XLSX.utils.book_new(); // Create a new workbook + + const dataMap = { + Personnel: 'personnel', + Overtime: 'overtime', + NonPersonnel: 'nonpersonnel', + Revenue: 'revenue' + }; + + const sheetData = { + Personnel: [], + Overtime: [], + NonPersonnel: [], + Revenue: [] + }; + + baseline.funds.forEach(fund => { + Object.keys(dataMap).forEach(sheetName => { + if (fund[dataMap[sheetName]] && fund[dataMap[sheetName]].table) { + sheetData[sheetName].push(...fund[dataMap[sheetName]].table); + } + }); + }); + + Object.keys(sheetData).forEach(sheetName => { + appendSheetToWorkbook(workbook, sheetData[sheetName], sheetName); + }); + + // Generate a downloadable file + const wbout = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }); + const blob = new Blob([wbout], { type: 'application/octet-stream' }); + + // Create a link and trigger the download + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = "baseline_data.xlsx"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +} \ No newline at end of file diff --git a/js/utils/data_utils/budget_data_handlers.js b/js/utils/data_utils/budget_data_handlers.js index a809fe3..598af9e 100644 --- a/js/utils/data_utils/budget_data_handlers.js +++ b/js/utils/data_utils/budget_data_handlers.js @@ -13,7 +13,6 @@ export const FundLookupTable = { if (!table[fund]){ // get fund name const fundName = fundData[fund][0]['Fund Name']; - console.log(fundName); // add fund to dictionary table[fund] = fundName; } diff --git a/js/views/01_upload/helpers.js b/js/views/01_upload/helpers.js index cec26e4..aadc069 100644 --- a/js/views/01_upload/helpers.js +++ b/js/views/01_upload/helpers.js @@ -2,8 +2,8 @@ import Subtitle from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' import NavButtons from '../../components/nav_buttons/nav_buttons.js' import Body from "../../components/body/body.js"; -import { fetchAndProcessExcel } from '../../utils/data_utils/XLSX_handlers.js'; import { DATA_ROOT } from '../../init.js'; +import { FileUpload } from '../../components/file_upload/file_upload.js'; export function initializePageView() { @@ -13,18 +13,13 @@ export function initializePageView() { // prepare page view Body.reset(); NavButtons.show(); + FileUpload.show(); // update page text Subtitle.update('Excel Upload'); + Prompt.Text.update(`Upload the baseline detail sheet given by your budget analyst.`); - // TODO: update to make upload actually work - Prompt.Text.update(`Placeholder for Excel Upload`); - Prompt.Buttons.Left.updateText('Upload'); - Prompt.Buttons.Left.show(); - Prompt.Buttons.Left.addAction(uploadExcelAction); -} - -async function uploadExcelAction() { - await fetchAndProcessExcel(DATA_ROOT + 'sample_detail_sheet.xlsx'); + // show and initialize file upload; enable continue after file saved in local storage + FileUpload.init(); NavButtons.Next.enable(); } \ No newline at end of file diff --git a/js/views/03_revenue/helpers.js b/js/views/03_revenue/helpers.js index 47d8d2e..06709c5 100644 --- a/js/views/03_revenue/helpers.js +++ b/js/views/03_revenue/helpers.js @@ -3,7 +3,7 @@ import { formatCurrency } from '../../utils/common_utils.js' import { REVENUE } from '../../init.js' import Body from '../../components/body/body.js' import NavButtons from '../../components/nav_buttons/nav_buttons.js' -import { pauseAndContinue } from '../view_logic.js' +import { nextPage } from '../view_logic.js' import Subtitle from '../../components/header/header.js' import Modal from '../../components/modal/modal.js' import Form from '../../components/form/form.js' @@ -23,7 +23,7 @@ export function preparePageView(){ export function setUpNavButtons(){ // clicking 'confirm' will also take us to the next page - Prompt.Buttons.Left.addAction(pauseAndContinue); + Prompt.Buttons.Left.addAction(nextPage); // TODO: allow user to edit revenue here Modal.Link.add('option2'); handleErrorComment(); @@ -31,7 +31,7 @@ export function setUpNavButtons(){ export function removeButtonEvents(){ // remove event listeners on prompt buttons - Prompt.Buttons.Left.removeAction(pauseAndContinue); + Prompt.Buttons.Left.removeAction(nextPage); Modal.Link.remove('option2'); } diff --git a/js/views/04.5_OT/helpers.js b/js/views/04.5_OT/helpers.js index d3e22d6..3f7ae0b 100644 --- a/js/views/04.5_OT/helpers.js +++ b/js/views/04.5_OT/helpers.js @@ -50,19 +50,22 @@ function OTRowOnEdit(){ } export async function initializeOTTable(){ - // load table data from json - await Table.Data.load(); - //after table is loaded, fill it - Table.show(); - Table.Columns.addAtEnd( '0', 'Hourly Employee Overtime (Wages)'); - Table.Columns.addAtEnd( '0', 'Salaried Employee Overtime (Salary)'); - // Table.Columns.addAtEnd( '0', 'Total Cost (including benefits)'); - Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, 'Edit');; - assignClasses(); - // add up the baseline costs and update sidebar - updateDisplayandTotals(); - // activate edit buttons - Table.Buttons.Edit.init(OTRowOnEdit, updateDisplayandTotals); + // load table data from local storage + if(await Table.Data.load()) { + //after table is loaded, fill it + Table.show(); + Table.Columns.addAtEnd( '0', 'Hourly Employee Overtime (Wages)'); + Table.Columns.addAtEnd( '0', 'Salaried Employee Overtime (Salary)'); + // Table.Columns.addAtEnd( '0', 'Total Cost (including benefits)'); + Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, 'Edit');; + assignClasses(); + // add up the baseline costs and update sidebar + updateDisplayandTotals(); + // activate edit buttons + Table.Buttons.Edit.init(OTRowOnEdit, updateDisplayandTotals); + } else { + Prompt.Text.update('No overtime expenses for this fund.') + } } function calculateTotalCost(wages, salary, fringe){ diff --git a/js/views/04_personnel/helpers.js b/js/views/04_personnel/helpers.js index 18af98c..712ff0a 100644 --- a/js/views/04_personnel/helpers.js +++ b/js/views/04_personnel/helpers.js @@ -53,17 +53,20 @@ function personnelRowOnEdit(){ } export async function initializePersonnelTable(){ - // load table data from json - await Table.Data.load(); - //after table is loaded, show it - Table.show(); - Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, 'Edit'); - assignClasses(); - // add up the baseline costs and update sidebar - updateDisplayandTotals(); - // activate edit buttons - Table.Buttons.Edit.init(personnelRowOnEdit, updateDisplayandTotals); - initializeRowAddition(); + // load table data from local storage + if(await Table.Data.load()) { + //after table is loaded, show it + Table.show(); + Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, 'Edit'); + assignClasses(); + // add up the baseline costs and update sidebar + updateDisplayandTotals(); + // activate edit buttons + Table.Buttons.Edit.init(personnelRowOnEdit, updateDisplayandTotals); + initializeRowAddition(); + } else { + Prompt.Text.update('No personnel expenses for this fund.') + } } function initializeRowAddition(){ @@ -89,7 +92,6 @@ function updateDisplayandTotals(){ // update total column Table.Cell.updateValue(rows[i], 'total-baseline', total_baseline_cost); - } // Save the table after all updates are done diff --git a/js/views/05_nonpersonnel/helpers.js b/js/views/05_nonpersonnel/helpers.js index 0939b9d..3cec10f 100644 --- a/js/views/05_nonpersonnel/helpers.js +++ b/js/views/05_nonpersonnel/helpers.js @@ -21,8 +21,8 @@ const nonPersonnelColumns = [ { title : 'Account String', className : 'account-string'}, { title : 'CPA #', className : 'cpa'}, { title : 'Contract End Date', className : 'contract-end'}, - { title: 'Recurring or One-Time', className: 'recurring'} - + { title: 'Recurring or One-Time', className: 'recurring'}, + { title: 'Object Name', className: 'object'} ]; export function preparePageView(){ @@ -41,15 +41,18 @@ export function preparePageView(){ } export async function initializeNonpersonnelTable(){ - // load table data from json - await Table.Data.load(); - //after table is loaded, fill it - Table.show(); - Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, "Edit"); - // assign cost classes - Table.Columns.assignClasses(nonPersonnelColumns); - // enable editing - Table.Buttons.Edit.init(nonPersonnelRowOnEdit, Table.save); + // load table data from storage + if(await Table.Data.load()) { + //after table is loaded, fill it + Table.show(); + Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, "Edit"); + // assign cost classes + Table.Columns.assignClasses(nonPersonnelColumns); + // enable editing + Table.Buttons.Edit.init(nonPersonnelRowOnEdit, Table.save); + } else { + Prompt.Text.update('No personnel expenses for this fund.') + } } function nonPersonnelRowOnEdit(){ diff --git a/js/views/06_new_initiatives/helpers.js b/js/views/06_new_initiatives/helpers.js index d5b74a1..6e8773f 100644 --- a/js/views/06_new_initiatives/helpers.js +++ b/js/views/06_new_initiatives/helpers.js @@ -5,9 +5,9 @@ import Form from '../../components/form/form.js' import Table from '../../components/table/table.js' import Body from '../../components/body/body.js' import NavButtons from '../../components/nav_buttons/nav_buttons.js' -import { pauseAndContinue } from '../view_logic.js' import Subtitle from '../../components/header/header.js' import Sidebar from '../../components/sidebar/sidebar.js' +import { nextPage } from '../view_logic.js' export function initializePageView() { // Prepare page view @@ -24,7 +24,7 @@ export function initializePageView() { Prompt.Buttons.Left.updateText('Yes'); Prompt.Buttons.Right.updateText('No'); // clicking 'no new initialitives' will also take us to the next page - Prompt.Buttons.Right.addAction(pauseAndContinue); + Prompt.Buttons.Right.addAction(nextPage); Prompt.Buttons.Left.addAction(NavButtons.Next.enable); } @@ -45,70 +45,73 @@ export function setUpForm() { ii). Why is the initiative needed? What is the value-add to residents? What is the Department’s plan for implementing the Initiative? iii). Why can’t the Initiative be funded with the Department’s baseline budget?`, 'Explanation', true); - Form.NewField.numericInput('What is your ballpark estimate of TOTAL ADDITONAL expenses associated with this initiative?', 'Revenue', false); + Form.NewField.numericInput('What is your ballpark estimate of TOTAL ADDITONAL expenses associated with this initiative?', 'Ballpark Total', false); Form.NewField.numericInput('Estimate of ADDITONAL personnel cost?', 'Personnel Cost', false); Form.NewField.numericInput('Estimate of ADDITONAL nonpersonnel cost?', 'Non-personnel Cost', false); Form.NewField.numericInput('Estimate of ADDITONAL revenue (if applicable)?', 'Revenue', false); - // TODO: implement this option - // Form.NewField.numericInput(`If you cannot answer the above, please provide a ballpark estimate of - // the TOTAL ADDITONAL cost associated with this initiative`, - // 'Ballpark Total', false); - Form.SubmitButton.add(); // Initialize form submission to table data Modal.Submit.init(handleNewInitSubmission); } -export function setUpTable() { - // Set up table - Table.clear(); - Table.adjustWidth('70%'); - Table.Buttons.AddRow.updateText('Add another new initiative'); -} - function assignClasses() { // record columns and their classes - const personnelColumns = [ + const initiativesCols = [ { title: 'Initiative Name', className: 'init-name' }, { title: `Explanation`, className: 'explanation' }, + { title: 'Ballpark Total', className: 'total', isCost: true }, { title: 'Revenue', className: 'revenue', isCost: true }, { title: 'Personnel Cost', className: 'personnel', isCost: true }, { title: 'Non-personnel Cost', className: 'nonpersonnel', isCost: true } ]; // assign cost classes - Table.Columns.assignClasses(personnelColumns) + Table.Columns.assignClasses(initiativesCols) +} + +export async function initializeInitTable(){ + // load table data from storage + if(await Table.Data.load()) { + //after table is loaded, fill it + assignClasses(); + Table.adjustWidth('70%'); + Table.Buttons.AddRow.updateText('Add another new initiative'); + tableView(); + } } -export function handleNewInitSubmission(event){ +function handleNewInitSubmission(event){ // get answers from form, hide form, show answers in table const responses = Form.fetchAllResponses(event); // make sure it's not an empty response if (Object.values(responses)[0] != ''){ - - // change page view - Modal.hide(); - Prompt.hide(); - // add data to table Table.Rows.add(responses); - assignClasses(); - Table.show(); - Table.Buttons.AddRow.show(); // save it Table.save(); + tableView(); } } +function tableView() { + // change page view + Table.show(); + Modal.hide(); + Prompt.hide(); + assignClasses(); + Table.Buttons.AddRow.show(); + NavButtons.Next.enable(); +} + export function removeModalLinks(){ Modal.Link.remove('option1'); Modal.Link.remove('add-btn'); } export function removePromptButtonListeners(){ - Prompt.Buttons.Right.removeAction(pauseAndContinue); + Prompt.Buttons.Right.removeAction(nextPage); Prompt.Buttons.Left.removeAction(NavButtons.Next.enable); Modal.clear(); } \ No newline at end of file diff --git a/js/views/06_new_initiatives/main.js b/js/views/06_new_initiatives/main.js index fb44921..90bf05c 100644 --- a/js/views/06_new_initiatives/main.js +++ b/js/views/06_new_initiatives/main.js @@ -1,5 +1,5 @@ -import { initializePageView, setUpModal, setUpForm, setUpTable, removeModalLinks, removePromptButtonListeners } from './helpers.js' +import { initializePageView, setUpModal, setUpForm, removeModalLinks, removePromptButtonListeners, initializeInitTable } from './helpers.js' import { CurrentPage } from '../../utils/data_utils/local_storage_handlers.js' @@ -9,7 +9,7 @@ export function loadNewInitiatives() { initializePageView(); setUpModal(); setUpForm(); - setUpTable(); + initializeInitTable(); } export function cleanUpInitiativesPage() { diff --git a/js/views/07_summary/main.js b/js/views/07_summary/main.js index 0b82682..60e200b 100644 --- a/js/views/07_summary/main.js +++ b/js/views/07_summary/main.js @@ -4,16 +4,17 @@ import Body from "../../components/body/body.js"; import Subtitle from "../../components/header/header.js"; import { visitPage } from "../view_logic.js"; import { Accordion } from "../../components/accordion/accordion.js"; +import { downloadXLSX } from "../../utils/data_utils/XLSX_handlers.js"; export function loadSummaryPage(){ //update page state CurrentPage.update('summary'); summaryView(); - // basicView(); } export function cleanUpSummaryPage(){ Prompt.Buttons.Right.removeAction(returnToWelcome); + Prompt.Buttons.Left.removeAction(downloadXLSX); } export function summaryView(){ @@ -29,8 +30,9 @@ export function summaryView(){ // update page text Subtitle.update('Summary'); - // TODO: update to make dynamic + // add button links Prompt.Buttons.Right.addAction(returnToWelcome); + Prompt.Buttons.Left.addAction(downloadXLSX); } diff --git a/js/views/view_logic.js b/js/views/view_logic.js index 70c5507..d4f2b76 100644 --- a/js/views/view_logic.js +++ b/js/views/view_logic.js @@ -78,9 +78,4 @@ export function lastPage(){ // go to that page visitPage(lastKey); } -} - -export async function pauseAndContinue(){ - await pauseExecution(0.5); - nextPage(); } \ No newline at end of file diff --git a/sample_data/sample_detail_sheet.xlsx b/sample_data/sample_detail_sheet.xlsx new file mode 100644 index 0000000..97f1c03 Binary files /dev/null and b/sample_data/sample_detail_sheet.xlsx differ