From 99733e89dca213ec5760cfdb8c4073608909e5c4 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 24 Jun 2024 14:01:15 -0400 Subject: [PATCH 01/30] fixed nav bug; parameterized more vars --- js/components/modal/modal.js | 6 ++++++ js/components/nav_buttons/nav_buttons.js | 4 ++-- js/components/sidebar/sidebar.js | 2 ++ js/init.js | 12 ++++++++---- js/pages/02_new_initiatives/helpers.js | 5 ++--- js/pages/03_revenue/main.js | 9 ++++++--- js/pages/04_personnel/helpers.js | 7 +------ 7 files changed, 27 insertions(+), 18 deletions(-) diff --git a/js/components/modal/modal.js b/js/components/modal/modal.js index 7f0275f..a28814f 100644 --- a/js/components/modal/modal.js +++ b/js/components/modal/modal.js @@ -12,6 +12,12 @@ export function addModalLink(button_id, modal_id){ }); } +export function removeModalLink(button_id, modal_id){ + document.getElementById(button_id).removeEventListener('click', function() { + showModal(modal_id); + }); +} + export function updateModalTitle(title){ document.getElementById('modal-title').textContent = title; } diff --git a/js/components/nav_buttons/nav_buttons.js b/js/components/nav_buttons/nav_buttons.js index ddce93f..0f3e8ce 100644 --- a/js/components/nav_buttons/nav_buttons.js +++ b/js/components/nav_buttons/nav_buttons.js @@ -29,7 +29,7 @@ export function initializeNavButtons(){ next_btn.addEventListener('click', nextPage); } -function nextPage(page_state){ +export function nextPage(){ var page_state = loadPageState(); const keys = Object.keys(pages); @@ -46,7 +46,7 @@ function nextPage(page_state){ } } -function lastPage(){ +export function lastPage(){ var page_state = loadPageState(); const keys = Object.keys(pages); diff --git a/js/components/sidebar/sidebar.js b/js/components/sidebar/sidebar.js index a48a90e..83010ce 100644 --- a/js/components/sidebar/sidebar.js +++ b/js/components/sidebar/sidebar.js @@ -1,4 +1,5 @@ import { formatCurrency } from "../../utils/utils.js"; +import { TARGET } from "../../init.js"; export function hideSideBar(){ document.getElementById('sidebar-panel').style.display = 'none'; @@ -9,6 +10,7 @@ export function showSideBar(){ document.getElementById('sidebar-panel').className = 'col-md-2'; document.getElementById('sidebar-panel').style.display = 'block'; document.getElementById('main-panel').className = 'col-md-10'; + addTarget(TARGET); } function updateSidebarStat(stat_id, new_figure){ diff --git a/js/init.js b/js/init.js index 38e5886..bcc1601 100644 --- a/js/init.js +++ b/js/init.js @@ -5,20 +5,24 @@ import { loadRevenuePage } from './pages/03_revenue/main.js' import { loadPageState } from './utils/storage-handlers.js' import { initializeNavButtons } from './components/nav_buttons/nav_buttons.js'; import { loadPersonnelPage } from './pages/04_personnel/main.js'; -import { addTarget } from './components/sidebar/sidebar.js'; // path for my laptop -//export let DATA_ROOT = '../../../data/law_dept_sample/' +export let DATA_ROOT = '../../../data/law_dept_sample/' // github path -export let DATA_ROOT = '../../budget-request-demo/data/law_dept_sample/' +// export let DATA_ROOT = '../../budget-request-demo/data/law_dept_sample/' export let REVENUE = 0; +export let TARGET = 2000000; + +// variables on the salary +export var fringe = 0.36 +export var cola = 0.02 +export var merit = 0.02 document.addEventListener('DOMContentLoaded', function () { var page_state = loadPageState(); initializeNavButtons(); - addTarget(2000000); switch (page_state){ case 'welcome': diff --git a/js/pages/02_new_initiatives/helpers.js b/js/pages/02_new_initiatives/helpers.js index 91b8267..5215606 100644 --- a/js/pages/02_new_initiatives/helpers.js +++ b/js/pages/02_new_initiatives/helpers.js @@ -2,8 +2,7 @@ import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { hidePrompt, showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction } from '../../components/prompt/prompt.js' -import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import { loadRevenuePage } from '../03_revenue/main.js' +import { showNavButtons, nextPage } from '../../components/nav_buttons/nav_buttons.js' import { addModalLink, updateModalTitle, clearModal, hideModal } from '../../components/modal/modal.js' import { fetchAllResponses, addTextarea, addTextInput, addNumericInput, addSubmitButtonToForm, addForm } from '../../components/form/form.js' import { adjustTableWidth, hideTable, clearTable, updateAddButtonText, addNewRow, showTable, showAddButton } from '../../components/table/table.js' @@ -51,7 +50,7 @@ export function setUpTable() { export function handleNavigation() { // clicking 'No' (no new initiatives) will also take us to the next page - addPromptButtonAction('option2', loadRevenuePage); + addPromptButtonAction('option2', nextPage); } export function handleFormSubmissions(event){ diff --git a/js/pages/03_revenue/main.js b/js/pages/03_revenue/main.js index 7d1c494..7761fc9 100644 --- a/js/pages/03_revenue/main.js +++ b/js/pages/03_revenue/main.js @@ -2,11 +2,11 @@ import { updatePageState } from '../../utils/storage-handlers.js' import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction } from '../../components/prompt/prompt.js' -import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import { loadNewInitiatives } from '../02_new_initiatives/main.js' +import { nextPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { hideTable } from '../../components/table/table.js' import { hideSideBar } from '../../components/sidebar/sidebar.js' import { formatCurrency } from '../../utils/utils.js' +import { removeModalLink } from '../../components/modal/modal.js' import { REVENUE } from '../../init.js' @@ -21,6 +21,7 @@ export function loadRevenuePage() { showNavButtons(); hideTable('main-table'); hideSideBar(); + removeModalLink('option1', 'main-modal'); // update page text updateSubtitle('Revenue Projections'); @@ -29,6 +30,8 @@ export function loadRevenuePage() { updatePromptButtons('Confirm and continue.', "This doesn't look right"); // clicking 'confirm and continue' will also take us to the next page - addPromptButtonAction('option1', loadNewInitiatives); + addPromptButtonAction('option1', nextPage); + // TODO: allow user to edit revenue here + addPromptButtonAction('option2', nextPage); } \ No newline at end of file diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index 206c537..49d1ea0 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -6,14 +6,9 @@ import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { AddCostClass, addCol, addColToEnd, addEditCol, adjustTableWidth, assignClassToColumn, showTable } from "../../components/table/table.js"; import { incrementSidebarStat, showSideBar } from "../../components/sidebar/sidebar.js"; import { formatCurrency } from "../../utils/utils.js"; -import { DATA_ROOT } from "../../init.js" +import { DATA_ROOT, fringe, cola, merit } from "../../init.js" import { createDropdownFromJSON } from "../../components/form/form.js"; -// variables on the salary -var fringe = 0.36 -var cola = 0.02 -var merit = 0.02 - export function preparePageView(){ // prepare page view hideWelcomeButtons(); From fdd86e8c95dc1de99accedb064e8397a294a4e63 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Tue, 25 Jun 2024 12:57:08 -0400 Subject: [PATCH 02/30] building out nonpersonnel form --- data/law_dept_sample/nonpersonnel_data.json | 20 +++ js/components/nav_buttons/nav_buttons.js | 19 +- js/components/sidebar/sidebar.js | 2 +- js/components/table/table.js | 18 +- js/init.js | 10 ++ js/pages/04_personnel/helpers.js | 13 +- .../{nonpersonnel-table.js => helpers.js} | 30 ---- js/pages/05_nonpersonnel/main.js | 167 ++++++++++++++++-- js/pages/05_nonpersonnel/personnel-table.js | 93 ---------- 9 files changed, 200 insertions(+), 172 deletions(-) create mode 100644 data/law_dept_sample/nonpersonnel_data.json rename js/pages/05_nonpersonnel/{nonpersonnel-table.js => helpers.js} (68%) delete mode 100644 js/pages/05_nonpersonnel/personnel-table.js diff --git a/data/law_dept_sample/nonpersonnel_data.json b/data/law_dept_sample/nonpersonnel_data.json new file mode 100644 index 0000000..dd844da --- /dev/null +++ b/data/law_dept_sample/nonpersonnel_data.json @@ -0,0 +1,20 @@ +[ + { + "Vendor": "Law Firm LLC", + "Account String": "1000-29320-320010", + "Object": 613100, + "Request Total": 100000 + }, + { + "Vendor": "Office Supplier", + "Account String": "1000-29320-320010", + "Object": 613100, + "Request Total": 10000 + }, + { + "Vendor": "Software Co.", + "Account String": "1000-29320-320010", + "Object": 613100, + "Request Total": 30000 + } +] \ No newline at end of file diff --git a/js/components/nav_buttons/nav_buttons.js b/js/components/nav_buttons/nav_buttons.js index 0f3e8ce..95082ab 100644 --- a/js/components/nav_buttons/nav_buttons.js +++ b/js/components/nav_buttons/nav_buttons.js @@ -1,15 +1,6 @@ -import { initializeWelcomePage } from '../../pages/00_welcome/main.js'; -import { loadNewInitiatives } from '../../pages/02_new_initiatives/main.js' -import { loadRevenuePage } from '../../pages/03_revenue/main.js' -import { loadPersonnelPage } from '../../pages/04_personnel/main.js'; import { loadPageState } from '../../utils/storage-handlers.js' - - -let pages = {'welcome' : initializeWelcomePage, - 'new-inits' : loadNewInitiatives, - 'revenue' : loadRevenuePage, - 'personnel' : loadPersonnelPage } +import { PAGES } from '../../init.js' export function hideNavButtons() { document.getElementById('nav-btns').style.display = 'none'; @@ -32,7 +23,7 @@ export function initializeNavButtons(){ export function nextPage(){ var page_state = loadPageState(); - const keys = Object.keys(pages); + const keys = Object.keys(PAGES); // Find the index of the current key const currentIndex = keys.indexOf(page_state); @@ -41,7 +32,7 @@ export function nextPage(){ if (currentIndex >= 0 && currentIndex < keys.length - 1) { // Get the next key const nextKey = keys[currentIndex + 1]; - const nextFn = pages[nextKey]; + const nextFn = PAGES[nextKey]; nextFn(); } } @@ -49,7 +40,7 @@ export function nextPage(){ export function lastPage(){ var page_state = loadPageState(); - const keys = Object.keys(pages); + const keys = Object.keys(PAGES); // Find the index of the current key const currentIndex = keys.indexOf(page_state); @@ -58,7 +49,7 @@ export function lastPage(){ if (currentIndex >= 1) { // Get the next key const lastKey = keys[currentIndex - 1]; - const lastFn = pages[lastKey]; + const lastFn = PAGES[lastKey]; lastFn(); } } \ No newline at end of file diff --git a/js/components/sidebar/sidebar.js b/js/components/sidebar/sidebar.js index 83010ce..9db497b 100644 --- a/js/components/sidebar/sidebar.js +++ b/js/components/sidebar/sidebar.js @@ -52,7 +52,7 @@ export function updateTotals() { document.querySelector('#baseline-total .stat').style.color = "green"; } if(baseline_total > target){ - document.getElementById('#baseline-total .stat').style.color = "red"; + document.querySelector('#baseline-total .stat').style.color = "red"; } } diff --git a/js/components/table/table.js b/js/components/table/table.js index 190a372..3460f24 100644 --- a/js/components/table/table.js +++ b/js/components/table/table.js @@ -137,10 +137,11 @@ export function addColToEnd(tableId, htmlContents = [], headerTitle = ''){ addCol(tableId, position, htmlContents, headerTitle); } +export let confirm_btn = ''; +export let edit_btn = ''; + // functions for editing rows function editButton() { - var edit_btn = ''; - var confirm_btn = ''; return edit_btn + confirm_btn; }; @@ -205,7 +206,14 @@ export function AddCostClass(tableId, headerName){ }); } - -export function updateCellValue(cell, newValue){ - pass; + +export function getCellValue(row, className){ + var cellValue = row.querySelector(`.${className}`).getAttribute('value'); + return parseFloat(cellValue); +} + +export function updateTableCell(row, col_class, new_value){ + const cell = row.querySelector(`.${col_class}`); + cell.setAttribute('value', new_value); + cell.textContent = formatCurrency(new_value); } \ No newline at end of file diff --git a/js/init.js b/js/init.js index bcc1601..e004aa2 100644 --- a/js/init.js +++ b/js/init.js @@ -5,6 +5,7 @@ import { loadRevenuePage } from './pages/03_revenue/main.js' import { loadPageState } from './utils/storage-handlers.js' import { initializeNavButtons } from './components/nav_buttons/nav_buttons.js'; import { loadPersonnelPage } from './pages/04_personnel/main.js'; +import { loadNonpersonnelPage } from './pages/05_nonpersonnel/main.js'; // path for my laptop export let DATA_ROOT = '../../../data/law_dept_sample/' @@ -19,6 +20,12 @@ export var fringe = 0.36 export var cola = 0.02 export var merit = 0.02 +export let PAGES = {'welcome' : initializeWelcomePage, + 'new-inits' : loadNewInitiatives, + 'revenue' : loadRevenuePage, + 'personnel' : loadPersonnelPage, + 'nonpersonnel' : loadNonpersonnelPage } + document.addEventListener('DOMContentLoaded', function () { var page_state = loadPageState(); @@ -37,6 +44,9 @@ document.addEventListener('DOMContentLoaded', function () { case 'personnel': loadPersonnelPage(); break; + case 'nonpersonnel': + loadNonpersonnelPage(); + break; }; diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index 49d1ea0..2f7653f 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -3,9 +3,8 @@ import { hidePromptButtons, showPrompt, updatePrompt } from "../../components/pr import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; -import { AddCostClass, addCol, addColToEnd, addEditCol, adjustTableWidth, assignClassToColumn, showTable } from "../../components/table/table.js"; +import { AddCostClass, addCol, addColToEnd, addEditCol, adjustTableWidth, assignClassToColumn, showTable, updateTableCell, getCellValue } from "../../components/table/table.js"; import { incrementSidebarStat, showSideBar } from "../../components/sidebar/sidebar.js"; -import { formatCurrency } from "../../utils/utils.js"; import { DATA_ROOT, fringe, cola, merit } from "../../init.js" import { createDropdownFromJSON } from "../../components/form/form.js"; @@ -131,21 +130,11 @@ function initializeConfirmButton(rowToEdit){ }); } -function getCellValue(row, className){ - var cellValue = row.querySelector(`.${className}`).getAttribute('value'); - return parseFloat(cellValue); -} function calculateTotalCost(ftes, avg_salary, fringe, cola, merit){ return ftes * avg_salary * (1 + fringe) * (1 + cola) * (1 + merit); } -export function updateTableCell(row, col_class, new_value){ - const cell = row.querySelector(`.${col_class}`); - cell.setAttribute('value', new_value); - cell.textContent = formatCurrency(new_value); -} - // update sidebar and also cost totals when the FTEs are edited function updateDisplayandTotals(){ // get row diff --git a/js/pages/05_nonpersonnel/nonpersonnel-table.js b/js/pages/05_nonpersonnel/helpers.js similarity index 68% rename from js/pages/05_nonpersonnel/nonpersonnel-table.js rename to js/pages/05_nonpersonnel/helpers.js index b8a7dd4..d9a591a 100644 --- a/js/pages/05_nonpersonnel/nonpersonnel-table.js +++ b/js/pages/05_nonpersonnel/helpers.js @@ -61,33 +61,3 @@ function handleActionClick(event) { updateDisplay(); } } - -// Add row for personnel table -function addRow() { - let table_id = "employee-table" - var table = document.getElementById(table_id); - var newRow = table.insertRow(-1); - // var newNameCell = newRow.insertCell(0); - // count number of table columns using jQuery - let key = "#" + table_id + " tr th"; - let cols = $(key).length; - for (let i = 0; i < cols-2; i++) { - var nextCell = newRow.insertCell(i); - createEditableCell(nextCell, 'value'); - } - - // Cost cell will always be second to last - var costCell = newRow.insertCell(cols-2); - createEditableCell(costCell, 'value', formatCurrency, updateDisplay, validateNumber); - costCell.classList.add('cost') - - // Last cell is the action cell with 3 buttons - var actionCell = newRow.insertCell(cols-1); - actionCell.innerHTML = ` -
- - - -
- `; -} \ No newline at end of file diff --git a/js/pages/05_nonpersonnel/main.js b/js/pages/05_nonpersonnel/main.js index 670e45f..8ef3568 100644 --- a/js/pages/05_nonpersonnel/main.js +++ b/js/pages/05_nonpersonnel/main.js @@ -1,24 +1,157 @@ -document.addEventListener('DOMContentLoaded', function () { +import { updatePageState } from "../../utils/storage-handlers.js"; - // Load from last local storage - loadTableData("employeeTableData"); - loadCounters(); +// helpers +import { hideWelcomeButtons } from "../../components/welcome/welcome.js"; +import { showPrompt, hidePromptButtons, updatePrompt } from "../../components/prompt/prompt.js"; +import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; +import { showSideBar, incrementSidebarStat } from "../../components/sidebar/sidebar.js"; +import { AddCostClass, addColToEnd, confirm_btn, edit_btn, adjustTableWidth, assignClassToColumn, showTable, getCellValue } from "../../components/table/table.js"; +import { updateSubtitle } from "../../components/header/header.js"; +import { loadJSONIntoTable } from "../../utils/data-handlers.js"; +import { DATA_ROOT } from "../../init.js"; +import { formatCurrency } from "../../utils/utils.js"; - // Add new row to the position table - document.querySelector('.btn-add').addEventListener('click', addRow); +export function loadNonpersonnelPage(){ - // Event listener for the action buttons - document.getElementById('employee-table').addEventListener('click', handleActionClick); + updatePageState('nonpersonnel'); + // prepare page view + hideWelcomeButtons(); + showPrompt(); + showNavButtons(); + showSideBar(); + hidePromptButtons(); + adjustTableWidth('main-table', '100%'); - // Add an event listener for the save button - // document.getElementById('save').addEventListener('click', function() { - // saveTableData("employee-table", 'employeeTableData'); - // }); + // update page text + updateSubtitle('Non-Personnel'); + updatePrompt('Select an action item for each non-personnel line item from last year.'); - // Add an event listener for the download button - document.getElementById('XLSX-download').addEventListener('click', function() { - saveTableData('employee-table', 'employeeTableData'); - downloadTableAsExcel('employeeTableData', 'Personnel', 'table-export'); + initializeNonpersonnelTable() +} + +var action_btn_html = ` + + + + + + ${edit_btn} + ${confirm_btn} + `; + +export async function initializeNonpersonnelTable(){ + // load table data from json + await loadJSONIntoTable(DATA_ROOT + 'nonpersonnel_data.json', 'main-table'); + //after table is loaded, fill it + showTable('main-table'); + addColToEnd('main-table', action_btn_html, "Select Action"); + // assign cost classes + assignClassToColumn('main-table', 'Request Total', 'request'); + AddCostClass('main-table', 'Request Total'); + // assignClassToColumn('main-table', 'Total Cost (Baseline)', 'total-baseline'); + // AddCostClass('main-table', 'Total Cost (Baseline)'); + // assignClassToColumn('main-table', 'Total Cost (Supplementary)', 'total-supp'); + // AddCostClass('main-table', 'Total Cost (Supplementary)'); + // // assign other classes + // assignClassToColumn('main-table', 'Job Name', 'job-name'); + // assignClassToColumn('main-table', 'Baseline FTEs', 'baseline-ftes'); + // assignClassToColumn('main-table', 'Supplemental FTEs', 'supp-ftes'); + // assignClassToColumn('main-table', 'Service', 'service'); + // manage edit buttons + handleRowEdit(['request']); +} + +export function handleRowEdit(editable_col_classes){ + // attach an event listener to each edit button in every row + var editButtons = document.getElementsByClassName('btn-edit'); + for (var i = 0; i < editButtons.length; i++) { + editButtons[i].addEventListener('click', async function(event) { + // Determine what was clicked on within the table + var rowToEdit = event.target.closest('tr'); + // mark row as being edited + rowToEdit.classList.add('active-editing'); + + // turn relevant entries into textboxes + for (let i = 0; i < editable_col_classes.length; i++){ + createEditableCell(editable_col_classes[i]) + } + + // hide edit buttons + var editButtons = document.getElementsByClassName('btn-edit'); + for (var i = 0; i < editButtons.length; i++) { + editButtons[i].style.display = 'none'; + } + + initializeConfirmButton(rowToEdit); + }); + }; +} + +function createEditableCell(cellClass, attribute = 'value'){ + // get cell + const cell = document.querySelector(`.active-editing td.${cellClass}`); + // Create an input element to edit the value + var textbox = document.createElement('input'); + textbox.type = 'text'; + if (cell.classList.contains('cost')){ + textbox.value = cell.getAttribute('value'); + } else { + textbox.value = cell.textContent; + } + // Clear the current content and append the textbox to the cell + cell.innerHTML = ''; + cell.appendChild(textbox); + //cell.appendChild(feedback); +} + +function initializeConfirmButton(rowToEdit){ + // get element and add listener for click + const confirm_btn = rowToEdit.querySelector(".btn-confirm"); + // show confirm button + confirm_btn.style.display = 'block'; + confirm_btn.addEventListener('click', function(event){ + // get current row + const rowToEdit = event.target.closest('tr'); + var textboxes = rowToEdit.querySelectorAll('input'); + // save all text in textboxes + textboxes.forEach( textbox => { + var enteredValue = textbox.value; + var cell = textbox.closest('td'); + cell.setAttribute('value', enteredValue); + if (cell.classList.contains('cost')){ + cell.textContent = formatCurrency(enteredValue); + } else { + cell.textContent = enteredValue; + } + }) + + // update values in sidebar + updateDisplayandTotals(); + + // make row no longer green + rowToEdit.classList.remove('active-editing'); + + // show edit buttons + var editButtons = document.getElementsByClassName('btn-edit'); + for (var i = 0; i < editButtons.length; i++) { + editButtons[i].style.display = 'block'; + } + + // hide confirm button + confirm_btn.style.display = 'none'; }); +} + +// update sidebar and also cost totals when the FTEs are edited +function updateDisplayandTotals(){ + // get row + const row = document.querySelector('.active-editing'); + console.log(row); + // fetch values for calculations + let request = getCellValue(row, 'request'); + + // update counters + incrementSidebarStat('baseline-nonpersonnel', request); + // incrementSidebarStat('supp-personnel', total_supp_cost); -}); \ No newline at end of file +} diff --git a/js/pages/05_nonpersonnel/personnel-table.js b/js/pages/05_nonpersonnel/personnel-table.js deleted file mode 100644 index 6b3abc7..0000000 --- a/js/pages/05_nonpersonnel/personnel-table.js +++ /dev/null @@ -1,93 +0,0 @@ - -// Manage clicks in the action area of the personnel table -function handleActionClick(event) { - // Determine what was clicked on within the table - var clickedElement = event.target; - - // Check if a delete button was clicked - if (clickedElement.matches('.btn-delete')) { - var currentRow = clickedElement.closest('tr'); - // get current class and update it - var rowClass = currentRow.className; - if (rowClass) { - currentRow.classList.remove(rowClass); - } - currentRow.classList.add("delete"); - // update variable counters - const salary = parseInt(event.target.closest('tr').querySelector('.cost').getAttribute('value')); - if (rowClass == "keep"){ - personnel_baseline -= salary; - } else if (rowClass == "supp"){ - personnel_supp -= salary; - }; - updateDisplay(); - } - // Check if a supplemental button was clicked - else if (clickedElement.matches('.btn-supplemental')) { - var currentRow = clickedElement.closest('tr'); - // get current class and update it - var rowClass = currentRow.className; - if (rowClass) { - currentRow.classList.remove(rowClass); - } - currentRow.classList.add("supp"); - // change counters - const salary = parseInt(event.target.closest('tr').querySelector('.cost').getAttribute('value')); - if (rowClass == "keep"){ - personnel_baseline -= salary - }; - if (rowClass != "supp"){ - personnel_supp += salary; - }; - updateDisplay(); - } - // Check if a carryover button was clicked - else if (clickedElement.matches('.btn-carryover')) { - var currentRow = clickedElement.closest('tr'); - // get current class and update it - var rowClass = currentRow.className; - if (rowClass) { - currentRow.classList.remove(rowClass); - } - currentRow.classList.add("keep"); - // update counter - const salary = parseInt(event.target.closest('tr').querySelector('.cost').getAttribute('value')); - if (rowClass == "supp"){ - personnel_supp -= salary; - } ; - if (rowClass != "keep"){ - personnel_baseline += salary; - } ; - updateDisplay(); - } -} - -// Add row for personnel table -function addRow() { - let table_id = "employee-table" - var table = document.getElementById(table_id); - var newRow = table.insertRow(-1); - // var newNameCell = newRow.insertCell(0); - // count number of table columns using jQuery - let key = "#" + table_id + " tr th"; - let cols = $(key).length; - for (let i = 0; i < cols-2; i++) { - var nextCell = newRow.insertCell(i); - createEditableCell(nextCell, 'value'); - } - - // Cost cell will always be second to last - var costCell = newRow.insertCell(cols-2); - createEditableCell(costCell, 'value', formatCurrency, updateDisplay, validateNumber); - costCell.classList.add('cost') - - // Last cell is the action cell with 3 buttons - var actionCell = newRow.insertCell(cols-1); - actionCell.innerHTML = ` -
- - - -
- `; -} \ No newline at end of file From fd3991b9e2666b6ca19d9d9c52d9fea3d0c46be4 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Tue, 25 Jun 2024 17:03:37 -0400 Subject: [PATCH 03/30] fixed some bugs; personnel adn nonpersonnel mostly working now --- js/components/form/form.js | 2 +- js/components/sidebar/sidebar.js | 2 +- js/components/table/table.js | 13 ++++-- js/pages/04_personnel/helpers.js | 72 ++++++++++++++++++----------- js/pages/05_nonpersonnel/helpers.js | 63 ------------------------- js/pages/05_nonpersonnel/main.js | 47 ++++++++----------- 6 files changed, 74 insertions(+), 125 deletions(-) delete mode 100644 js/pages/05_nonpersonnel/helpers.js diff --git a/js/components/form/form.js b/js/components/form/form.js index 9081a0f..3e7620d 100644 --- a/js/components/form/form.js +++ b/js/components/form/form.js @@ -132,5 +132,5 @@ export async function createDropdownFromJSON(json_path) { }); // Return the select element so it can be appended to the document - return selectElement.outerHTML; + return selectElement; } \ No newline at end of file diff --git a/js/components/sidebar/sidebar.js b/js/components/sidebar/sidebar.js index 9db497b..adf6ce8 100644 --- a/js/components/sidebar/sidebar.js +++ b/js/components/sidebar/sidebar.js @@ -13,7 +13,7 @@ export function showSideBar(){ addTarget(TARGET); } -function updateSidebarStat(stat_id, new_figure){ +export function updateSidebarStat(stat_id, new_figure){ replaceSidebarStat(stat_id, new_figure); // TODO: save in memory updateTotals(); diff --git a/js/components/table/table.js b/js/components/table/table.js index 3460f24..65a2a75 100644 --- a/js/components/table/table.js +++ b/js/components/table/table.js @@ -138,7 +138,8 @@ export function addColToEnd(tableId, htmlContents = [], headerTitle = ''){ } export let confirm_btn = ''; -export let edit_btn = ''; +export let edit_btn = ''; +export let delete_btn = ''; // functions for editing rows function editButton() { @@ -207,13 +208,15 @@ export function AddCostClass(tableId, headerName){ } -export function getCellValue(row, className){ - var cellValue = row.querySelector(`.${className}`).getAttribute('value'); - return parseFloat(cellValue); +// return cell value attribute or 0 if it does not exist +export function getCellValue(row, className) { + var cell = row.querySelector(`.${className}`); + var cellValue = cell ? cell.getAttribute('value') : null; + return cellValue ? parseFloat(cellValue) : 0; } export function updateTableCell(row, col_class, new_value){ const cell = row.querySelector(`.${col_class}`); cell.setAttribute('value', new_value); cell.textContent = formatCurrency(new_value); -} \ No newline at end of file +} diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index 2f7653f..d61b3b8 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -4,10 +4,11 @@ import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { AddCostClass, addCol, addColToEnd, addEditCol, adjustTableWidth, assignClassToColumn, showTable, updateTableCell, getCellValue } from "../../components/table/table.js"; -import { incrementSidebarStat, showSideBar } from "../../components/sidebar/sidebar.js"; +import { incrementSidebarStat, showSideBar, updateSidebarStat } from "../../components/sidebar/sidebar.js"; import { DATA_ROOT, fringe, cola, merit } from "../../init.js" import { createDropdownFromJSON } from "../../components/form/form.js"; + export function preparePageView(){ // prepare page view hideWelcomeButtons(); @@ -60,9 +61,7 @@ export function handleRowEdit(){ // turn relevant entries into textboxes createEditableCell('baseline-ftes'); createEditableCell('supp-ftes'); - // add service dropdown - const serviceDropdown = await createDropdownFromJSON(DATA_ROOT + 'services.json'); - rowToEdit.querySelector('.service').innerHTML = serviceDropdown; + createSelectCell('service'); // hide edit buttons var editButtons = document.getElementsByClassName('btn-edit'); @@ -89,6 +88,16 @@ function createEditableCell(cellClass, attribute = 'value'){ //cell.appendChild(feedback); } +async function createSelectCell(cellClass){ + // get cell + const cell = document.querySelector(`.active-editing td.${cellClass}`); + // add service dropdown + const serviceDropdown = await createDropdownFromJSON(DATA_ROOT + 'services.json'); + serviceDropdown.value = cell.textContent; + // Clear the current content and append the textbox to the cell + cell.innerHTML = ''; + cell.appendChild(serviceDropdown); +} function initializeConfirmButton(rowToEdit){ // get element and add listener for click @@ -98,6 +107,14 @@ function initializeConfirmButton(rowToEdit){ confirm_btn.addEventListener('click', function(event){ // get current row const rowToEdit = event.target.closest('tr'); + + // set service selection + const serviceSelector = rowToEdit.querySelector('select'); + if (serviceSelector){ + var cell = serviceSelector.closest('td'); + cell.textContent = serviceSelector.value; + } + var textboxes = rowToEdit.querySelectorAll('input'); // save all text in textboxes textboxes.forEach( textbox => { @@ -106,12 +123,6 @@ function initializeConfirmButton(rowToEdit){ cell.textContent = enteredValue; cell.setAttribute('value', enteredValue); }) - // set service selection - const serviceSelector = rowToEdit.querySelector('select'); - var cell = serviceSelector.parentElement; - cell.textContent = serviceSelector.value; - //set service value - // update values in sidebar updateDisplayandTotals(); @@ -137,22 +148,27 @@ function calculateTotalCost(ftes, avg_salary, fringe, cola, merit){ // update sidebar and also cost totals when the FTEs are edited function updateDisplayandTotals(){ - // get row - const row = document.querySelector('.active-editing'); - // fetch values for calculations - let avg_salary = getCellValue(row, 'avg-salary'); - let baseline_ftes = getCellValue(row, 'baseline-ftes'); - let supp_ftes = getCellValue(row, 'supp-ftes'); - - // calcuate #FTEs x average salary + COLA adjustments + merit adjustments + fringe - let total_baseline_cost = calculateTotalCost(baseline_ftes, avg_salary, fringe, cola, merit); - let total_supp_cost = calculateTotalCost(supp_ftes, avg_salary, fringe, cola, merit); - - // update counters - incrementSidebarStat('baseline-personnel', total_baseline_cost); - incrementSidebarStat('supp-personnel', total_supp_cost); - - // update totals in table - updateTableCell(row, 'total-baseline', total_baseline_cost); - updateTableCell(row, 'total-supp', total_supp_cost); + // initialize + updateSidebarStat('baseline-personnel', 0); + updateSidebarStat('supp-personnel', 0); + // calculate for each row + let rows = document.getElementsByTagName('tr'); + for (let i = 1; i < rows.length; i++){ + // fetch values for calculations + let avg_salary = getCellValue(rows[i], 'avg-salary'); + let baseline_ftes = getCellValue(rows[i], 'baseline-ftes'); + let supp_ftes = getCellValue(rows[i], 'supp-ftes'); + + // calcuate #FTEs x average salary + COLA adjustments + merit adjustments + fringe + let total_baseline_cost = calculateTotalCost(baseline_ftes, avg_salary, fringe, cola, merit); + let total_supp_cost = calculateTotalCost(supp_ftes, avg_salary, fringe, cola, merit); + + // update counters + incrementSidebarStat('baseline-personnel', total_baseline_cost); + incrementSidebarStat('supp-personnel', total_supp_cost); + + // update totals in table + updateTableCell(rows[i], 'total-baseline', total_baseline_cost); + updateTableCell(rows[i], 'total-supp', total_supp_cost); + } } diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/pages/05_nonpersonnel/helpers.js deleted file mode 100644 index d9a591a..0000000 --- a/js/pages/05_nonpersonnel/helpers.js +++ /dev/null @@ -1,63 +0,0 @@ - -// Manage clicks in the action area of the personnel table -function handleActionClick(event) { - // Determine what was clicked on within the table - var clickedElement = event.target; - - // Check if a delete button was clicked - if (clickedElement.matches('.btn-delete')) { - var currentRow = clickedElement.closest('tr'); - // get current class and update it - var rowClass = currentRow.className; - if (rowClass) { - currentRow.classList.remove(rowClass); - } - currentRow.classList.add("delete"); - // update variable counters - const salary = parseInt(event.target.closest('tr').querySelector('.cost').getAttribute('value')); - if (rowClass == "keep"){ - nonpersonnel_baseline -= salary; - } else if (rowClass == "supp"){ - nonpersonnel_supp -= salary; - }; - updateDisplay(); - } - // Check if a supplemental button was clicked - else if (clickedElement.matches('.btn-supplemental')) { - var currentRow = clickedElement.closest('tr'); - // get current class and update it - var rowClass = currentRow.className; - if (rowClass) { - currentRow.classList.remove(rowClass); - } - currentRow.classList.add("supp"); - // change counters - const salary = parseInt(event.target.closest('tr').querySelector('.cost').getAttribute('value')); - if (rowClass == "keep"){ - nonpersonnel_baseline -= salary - }; - if (rowClass != "supp"){ - nonpersonnel_supp += salary; - }; - updateDisplay(); - } - // Check if a carryover button was clicked - else if (clickedElement.matches('.btn-carryover')) { - var currentRow = clickedElement.closest('tr'); - // get current class and update it - var rowClass = currentRow.className; - if (rowClass) { - currentRow.classList.remove(rowClass); - } - currentRow.classList.add("keep"); - // update counter - const salary = parseInt(event.target.closest('tr').querySelector('.cost').getAttribute('value')); - if (rowClass == "supp"){ - nonpersonnel_supp -= salary; - } ; - if (rowClass != "keep"){ - nonpersonnel_baseline += salary; - } ; - updateDisplay(); - } -} diff --git a/js/pages/05_nonpersonnel/main.js b/js/pages/05_nonpersonnel/main.js index 8ef3568..6791753 100644 --- a/js/pages/05_nonpersonnel/main.js +++ b/js/pages/05_nonpersonnel/main.js @@ -4,8 +4,8 @@ import { updatePageState } from "../../utils/storage-handlers.js"; import { hideWelcomeButtons } from "../../components/welcome/welcome.js"; import { showPrompt, hidePromptButtons, updatePrompt } from "../../components/prompt/prompt.js"; import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; -import { showSideBar, incrementSidebarStat } from "../../components/sidebar/sidebar.js"; -import { AddCostClass, addColToEnd, confirm_btn, edit_btn, adjustTableWidth, assignClassToColumn, showTable, getCellValue } from "../../components/table/table.js"; +import { showSideBar, incrementSidebarStat, updateSidebarStat } from "../../components/sidebar/sidebar.js"; +import { AddCostClass, addColToEnd, confirm_btn, edit_btn, delete_btn, adjustTableWidth, assignClassToColumn, showTable, getCellValue } from "../../components/table/table.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; @@ -29,22 +29,12 @@ export function loadNonpersonnelPage(){ initializeNonpersonnelTable() } -var action_btn_html = ` - - - - - - ${edit_btn} - ${confirm_btn} - `; - export async function initializeNonpersonnelTable(){ // load table data from json await loadJSONIntoTable(DATA_ROOT + 'nonpersonnel_data.json', 'main-table'); //after table is loaded, fill it showTable('main-table'); - addColToEnd('main-table', action_btn_html, "Select Action"); + addColToEnd('main-table', `${delete_btn} ${edit_btn} ${confirm_btn}`, "Select Action"); // assign cost classes assignClassToColumn('main-table', 'Request Total', 'request'); AddCostClass('main-table', 'Request Total'); @@ -87,6 +77,8 @@ export function handleRowEdit(editable_col_classes){ }; } +// on edit, hide delete button. On + function createEditableCell(cellClass, attribute = 'value'){ // get cell const cell = document.querySelector(`.active-editing td.${cellClass}`); @@ -105,10 +97,10 @@ function createEditableCell(cellClass, attribute = 'value'){ } function initializeConfirmButton(rowToEdit){ - // get element and add listener for click - const confirm_btn = rowToEdit.querySelector(".btn-confirm"); // show confirm button - confirm_btn.style.display = 'block'; + const confirm_btn = rowToEdit.querySelector(".btn-confirm"); + confirm_btn.style.display = 'inline'; + // add event listener for confirm confirm_btn.addEventListener('click', function(event){ // get current row const rowToEdit = event.target.closest('tr'); @@ -134,7 +126,7 @@ function initializeConfirmButton(rowToEdit){ // show edit buttons var editButtons = document.getElementsByClassName('btn-edit'); for (var i = 0; i < editButtons.length; i++) { - editButtons[i].style.display = 'block'; + editButtons[i].style.display = 'inline'; } // hide confirm button @@ -144,14 +136,15 @@ function initializeConfirmButton(rowToEdit){ // update sidebar and also cost totals when the FTEs are edited function updateDisplayandTotals(){ - // get row - const row = document.querySelector('.active-editing'); - console.log(row); - // fetch values for calculations - let request = getCellValue(row, 'request'); - - // update counters - incrementSidebarStat('baseline-nonpersonnel', request); - // incrementSidebarStat('supp-personnel', total_supp_cost); - + // initialize + updateSidebarStat('baseline-nonpersonnel', 0); + // calculate for each row + let rows = document.getElementsByTagName('tr'); + for (let i = 1; i < rows.length; i++){ + // fetch values for calculations + let request = getCellValue(rows[i], 'request'); + + // update counters + incrementSidebarStat('baseline-nonpersonnel', request); + } } From 7ecfc29ba67cbc3c12cfc8bae652ecdc18187267 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Wed, 26 Jun 2024 09:33:20 -0400 Subject: [PATCH 04/30] moved functions to helper file for now; still need to do a reorg --- js/pages/05_nonpersonnel/helpers.js | 143 +++++++++++++++++++++++++++ js/pages/05_nonpersonnel/main.js | 145 +--------------------------- 2 files changed, 145 insertions(+), 143 deletions(-) create mode 100644 js/pages/05_nonpersonnel/helpers.js diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/pages/05_nonpersonnel/helpers.js new file mode 100644 index 0000000..421290b --- /dev/null +++ b/js/pages/05_nonpersonnel/helpers.js @@ -0,0 +1,143 @@ +import { hideWelcomeButtons } from "../../components/welcome/welcome.js"; +import { showPrompt, hidePromptButtons, updatePrompt } from "../../components/prompt/prompt.js"; +import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; +import { showSideBar, incrementSidebarStat, updateSidebarStat } from "../../components/sidebar/sidebar.js"; +import { AddCostClass, addColToEnd, confirm_btn, edit_btn, delete_btn, adjustTableWidth, assignClassToColumn, showTable, getCellValue } from "../../components/table/table.js"; +import { updateSubtitle } from "../../components/header/header.js"; +import { loadJSONIntoTable } from "../../utils/data-handlers.js"; +import { DATA_ROOT } from "../../init.js"; +import { formatCurrency } from "../../utils/utils.js"; + +export function preparePageView(){ + // prepare page view + hideWelcomeButtons(); + showPrompt(); + showNavButtons(); + showSideBar(); + hidePromptButtons(); + adjustTableWidth('main-table', '100%'); + // update page text + updateSubtitle('Non-Personnel'); + updatePrompt('Select an action item for each non-personnel line item from last year.'); + updateDisplayandTotals(); +} + +export async function initializeNonpersonnelTable(){ + // load table data from json + await loadJSONIntoTable(DATA_ROOT + 'nonpersonnel_data.json', 'main-table'); + //after table is loaded, fill it + showTable('main-table'); + addColToEnd('main-table', `${delete_btn} ${edit_btn} ${confirm_btn}`, "Select Action"); + // assign cost classes + assignClassToColumn('main-table', 'Request Total', 'request'); + AddCostClass('main-table', 'Request Total'); + // assignClassToColumn('main-table', 'Total Cost (Baseline)', 'total-baseline'); + // AddCostClass('main-table', 'Total Cost (Baseline)'); + // assignClassToColumn('main-table', 'Total Cost (Supplementary)', 'total-supp'); + // AddCostClass('main-table', 'Total Cost (Supplementary)'); + // // assign other classes + // assignClassToColumn('main-table', 'Job Name', 'job-name'); + // assignClassToColumn('main-table', 'Baseline FTEs', 'baseline-ftes'); + // assignClassToColumn('main-table', 'Supplemental FTEs', 'supp-ftes'); + // assignClassToColumn('main-table', 'Service', 'service'); + // manage edit buttons + handleRowEdit(['request']); +} + +export function handleRowEdit(editable_col_classes){ + // attach an event listener to each edit button in every row + var editButtons = document.getElementsByClassName('btn-edit'); + for (var i = 0; i < editButtons.length; i++) { + editButtons[i].addEventListener('click', async function(event) { + // Determine what was clicked on within the table + var rowToEdit = event.target.closest('tr'); + // mark row as being edited + rowToEdit.classList.add('active-editing'); + + // turn relevant entries into textboxes + for (let i = 0; i < editable_col_classes.length; i++){ + createEditableCell(editable_col_classes[i]) + } + + // hide edit buttons + var editButtons = document.getElementsByClassName('btn-edit'); + for (var i = 0; i < editButtons.length; i++) { + editButtons[i].style.display = 'none'; + } + + initializeConfirmButton(rowToEdit); + }); + }; +} + +// on edit, hide delete button. On + +function createEditableCell(cellClass, attribute = 'value'){ + // get cell + const cell = document.querySelector(`.active-editing td.${cellClass}`); + // Create an input element to edit the value + var textbox = document.createElement('input'); + textbox.type = 'text'; + if (cell.classList.contains('cost')){ + textbox.value = cell.getAttribute('value'); + } else { + textbox.value = cell.textContent; + } + // Clear the current content and append the textbox to the cell + cell.innerHTML = ''; + cell.appendChild(textbox); + //cell.appendChild(feedback); +} + +function initializeConfirmButton(rowToEdit){ + // show confirm button + const confirm_btn = rowToEdit.querySelector(".btn-confirm"); + confirm_btn.style.display = 'inline'; + // add event listener for confirm + confirm_btn.addEventListener('click', function(event){ + // get current row + const rowToEdit = event.target.closest('tr'); + var textboxes = rowToEdit.querySelectorAll('input'); + // save all text in textboxes + textboxes.forEach( textbox => { + var enteredValue = textbox.value; + var cell = textbox.closest('td'); + cell.setAttribute('value', enteredValue); + if (cell.classList.contains('cost')){ + cell.textContent = formatCurrency(enteredValue); + } else { + cell.textContent = enteredValue; + } + }) + + // update values in sidebar + updateDisplayandTotals(); + + // make row no longer green + rowToEdit.classList.remove('active-editing'); + + // show edit buttons + var editButtons = document.getElementsByClassName('btn-edit'); + for (var i = 0; i < editButtons.length; i++) { + editButtons[i].style.display = 'inline'; + } + + // hide confirm button + confirm_btn.style.display = 'none'; + }); +} + +// update sidebar and also cost totals when the FTEs are edited +function updateDisplayandTotals(){ + // initialize + updateSidebarStat('baseline-nonpersonnel', 0); + // calculate for each row + let rows = document.getElementsByTagName('tr'); + for (let i = 1; i < rows.length; i++){ + // fetch values for calculations + let request = getCellValue(rows[i], 'request'); + + // update counters + incrementSidebarStat('baseline-nonpersonnel', request); + } +} diff --git a/js/pages/05_nonpersonnel/main.js b/js/pages/05_nonpersonnel/main.js index 6791753..6e2e72f 100644 --- a/js/pages/05_nonpersonnel/main.js +++ b/js/pages/05_nonpersonnel/main.js @@ -1,150 +1,9 @@ import { updatePageState } from "../../utils/storage-handlers.js"; - -// helpers -import { hideWelcomeButtons } from "../../components/welcome/welcome.js"; -import { showPrompt, hidePromptButtons, updatePrompt } from "../../components/prompt/prompt.js"; -import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; -import { showSideBar, incrementSidebarStat, updateSidebarStat } from "../../components/sidebar/sidebar.js"; -import { AddCostClass, addColToEnd, confirm_btn, edit_btn, delete_btn, adjustTableWidth, assignClassToColumn, showTable, getCellValue } from "../../components/table/table.js"; -import { updateSubtitle } from "../../components/header/header.js"; -import { loadJSONIntoTable } from "../../utils/data-handlers.js"; -import { DATA_ROOT } from "../../init.js"; -import { formatCurrency } from "../../utils/utils.js"; +import { preparePageView, initializeNonpersonnelTable } from "../05_nonpersonnel/helpers.js"; export function loadNonpersonnelPage(){ updatePageState('nonpersonnel'); - // prepare page view - hideWelcomeButtons(); - showPrompt(); - showNavButtons(); - showSideBar(); - hidePromptButtons(); - adjustTableWidth('main-table', '100%'); - - // update page text - updateSubtitle('Non-Personnel'); - updatePrompt('Select an action item for each non-personnel line item from last year.'); - + preparePageView(); initializeNonpersonnelTable() } - -export async function initializeNonpersonnelTable(){ - // load table data from json - await loadJSONIntoTable(DATA_ROOT + 'nonpersonnel_data.json', 'main-table'); - //after table is loaded, fill it - showTable('main-table'); - addColToEnd('main-table', `${delete_btn} ${edit_btn} ${confirm_btn}`, "Select Action"); - // assign cost classes - assignClassToColumn('main-table', 'Request Total', 'request'); - AddCostClass('main-table', 'Request Total'); - // assignClassToColumn('main-table', 'Total Cost (Baseline)', 'total-baseline'); - // AddCostClass('main-table', 'Total Cost (Baseline)'); - // assignClassToColumn('main-table', 'Total Cost (Supplementary)', 'total-supp'); - // AddCostClass('main-table', 'Total Cost (Supplementary)'); - // // assign other classes - // assignClassToColumn('main-table', 'Job Name', 'job-name'); - // assignClassToColumn('main-table', 'Baseline FTEs', 'baseline-ftes'); - // assignClassToColumn('main-table', 'Supplemental FTEs', 'supp-ftes'); - // assignClassToColumn('main-table', 'Service', 'service'); - // manage edit buttons - handleRowEdit(['request']); -} - -export function handleRowEdit(editable_col_classes){ - // attach an event listener to each edit button in every row - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].addEventListener('click', async function(event) { - // Determine what was clicked on within the table - var rowToEdit = event.target.closest('tr'); - // mark row as being edited - rowToEdit.classList.add('active-editing'); - - // turn relevant entries into textboxes - for (let i = 0; i < editable_col_classes.length; i++){ - createEditableCell(editable_col_classes[i]) - } - - // hide edit buttons - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].style.display = 'none'; - } - - initializeConfirmButton(rowToEdit); - }); - }; -} - -// on edit, hide delete button. On - -function createEditableCell(cellClass, attribute = 'value'){ - // get cell - const cell = document.querySelector(`.active-editing td.${cellClass}`); - // Create an input element to edit the value - var textbox = document.createElement('input'); - textbox.type = 'text'; - if (cell.classList.contains('cost')){ - textbox.value = cell.getAttribute('value'); - } else { - textbox.value = cell.textContent; - } - // Clear the current content and append the textbox to the cell - cell.innerHTML = ''; - cell.appendChild(textbox); - //cell.appendChild(feedback); -} - -function initializeConfirmButton(rowToEdit){ - // show confirm button - const confirm_btn = rowToEdit.querySelector(".btn-confirm"); - confirm_btn.style.display = 'inline'; - // add event listener for confirm - confirm_btn.addEventListener('click', function(event){ - // get current row - const rowToEdit = event.target.closest('tr'); - var textboxes = rowToEdit.querySelectorAll('input'); - // save all text in textboxes - textboxes.forEach( textbox => { - var enteredValue = textbox.value; - var cell = textbox.closest('td'); - cell.setAttribute('value', enteredValue); - if (cell.classList.contains('cost')){ - cell.textContent = formatCurrency(enteredValue); - } else { - cell.textContent = enteredValue; - } - }) - - // update values in sidebar - updateDisplayandTotals(); - - // make row no longer green - rowToEdit.classList.remove('active-editing'); - - // show edit buttons - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].style.display = 'inline'; - } - - // hide confirm button - confirm_btn.style.display = 'none'; - }); -} - -// update sidebar and also cost totals when the FTEs are edited -function updateDisplayandTotals(){ - // initialize - updateSidebarStat('baseline-nonpersonnel', 0); - // calculate for each row - let rows = document.getElementsByTagName('tr'); - for (let i = 1; i < rows.length; i++){ - // fetch values for calculations - let request = getCellValue(rows[i], 'request'); - - // update counters - incrementSidebarStat('baseline-nonpersonnel', request); - } -} From 039bed5f901d0d5d93d227815b04d40b899c5a67 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Wed, 26 Jun 2024 13:39:10 -0400 Subject: [PATCH 05/30] building out sequence; adding fund data and tabler --- data/law_dept_sample/funds.json | 6 ++ index.html | 58 ++++++------------- js/components/welcome/welcome.css | 16 +++-- js/components/welcome/welcome.js | 2 +- js/init.js | 40 ++++++------- js/pages/00_welcome/helpers.js | 8 ++- js/pages/02_baseline_landing_page/main.js | 34 +++++++++++ js/pages/03_revenue/main.js | 2 +- js/pages/04.5_OT/main.js | 26 +++++++++ .../helpers.js | 4 +- .../main.js | 0 js/pages/07_summary/main.js | 30 ++++++++++ 12 files changed, 148 insertions(+), 78 deletions(-) create mode 100644 data/law_dept_sample/funds.json create mode 100644 js/pages/02_baseline_landing_page/main.js create mode 100644 js/pages/04.5_OT/main.js rename js/pages/{02_new_initiatives => 06_new_initiatives}/helpers.js (91%) rename js/pages/{02_new_initiatives => 06_new_initiatives}/main.js (100%) create mode 100644 js/pages/07_summary/main.js diff --git a/data/law_dept_sample/funds.json b/data/law_dept_sample/funds.json new file mode 100644 index 0000000..dfe4c80 --- /dev/null +++ b/data/law_dept_sample/funds.json @@ -0,0 +1,6 @@ +[ + { "ID" : "1000", + "Name" : "General Fund"}, + { "ID" : "29320", + "NAme" : "Sample Grant Fund"} +] \ No newline at end of file diff --git a/index.html b/index.html index a1e2c85..077bf02 100644 --- a/index.html +++ b/index.html @@ -38,55 +38,33 @@


+ + +
+ + + + +
+
- - -


- +
diff --git a/js/components/welcome/welcome.css b/js/components/welcome/welcome.css index 23b5108..e30c686 100644 --- a/js/components/welcome/welcome.css +++ b/js/components/welcome/welcome.css @@ -1,11 +1,11 @@ /* Welcome page (index.html) */ .step { - width: 90%; - height: 100px; + width: 60%; + height: 80px; font-size: 1.75em; - /* margin-top: 10px; */ - margin-bottom: 10px; + margin-bottom: 0px; /* Adds spacing between buttons */ + margin-left: 20%; } #step-upload { background-color: var(--blue);} @@ -17,12 +17,10 @@ .step:hover { color: white; - opacity: 50%;; + opacity: 50%; } #welcome-page { - display: flex; - justify-content: center; /* Center horizontally */ - align-items: center; /* Center vertically */ - flex-wrap: wrap; + justify-content: center; + align-items: center; } \ No newline at end of file diff --git a/js/components/welcome/welcome.js b/js/components/welcome/welcome.js index eebfb33..2242300 100644 --- a/js/components/welcome/welcome.js +++ b/js/components/welcome/welcome.js @@ -1,6 +1,6 @@ // Hide and unhide welcome buttons export function unhideWelcomeButtons(){ - document.getElementById("welcome-page").style.display = "flex"; + document.getElementById("welcome-page").style.display = "block"; } export function hideWelcomeButtons(){ document.getElementById("welcome-page").style.display = "none"; diff --git a/js/init.js b/js/init.js index e004aa2..66d6fc7 100644 --- a/js/init.js +++ b/js/init.js @@ -1,11 +1,14 @@ // import functions import { initializeWelcomePage } from './pages/00_welcome/main.js'; -import { loadNewInitiatives } from './pages/02_new_initiatives/main.js' +import { loadNewInitiatives } from './pages/06_new_initiatives/main.js' import { loadRevenuePage } from './pages/03_revenue/main.js' import { loadPageState } from './utils/storage-handlers.js' import { initializeNavButtons } from './components/nav_buttons/nav_buttons.js'; import { loadPersonnelPage } from './pages/04_personnel/main.js'; +import { loadOTPage } from './pages/04.5_OT/main.js'; import { loadNonpersonnelPage } from './pages/05_nonpersonnel/main.js'; +import { loadBaselineLandingPage } from './pages/02_baseline_landing_page/main.js'; +import { loadSummaryPage } from './pages/07_summary/main.js'; // path for my laptop export let DATA_ROOT = '../../../data/law_dept_sample/' @@ -21,33 +24,24 @@ export var cola = 0.02 export var merit = 0.02 export let PAGES = {'welcome' : initializeWelcomePage, - 'new-inits' : loadNewInitiatives, + 'baseline-landing' : loadBaselineLandingPage, 'revenue' : loadRevenuePage, 'personnel' : loadPersonnelPage, - 'nonpersonnel' : loadNonpersonnelPage } + 'overtime' : loadOTPage, + 'nonpersonnel' : loadNonpersonnelPage, + 'new-inits' : loadNewInitiatives, + 'summary' : loadSummaryPage } document.addEventListener('DOMContentLoaded', function () { - var page_state = loadPageState(); initializeNavButtons(); - switch (page_state){ - case 'welcome': - initializeWelcomePage(); - break; - case 'new-inits': - loadNewInitiatives(); - break; - case 'revenue': - loadRevenuePage(); - break; - case 'personnel': - loadPersonnelPage(); - break; - case 'nonpersonnel': - loadNonpersonnelPage(); - break; - }; - - + // Use the page_state to access and call the corresponding function from PAGES + if (PAGES[page_state]) { + PAGES[page_state](); // Invokes the function if it exists in the PAGES map + } else { + console.error(`No page initializer found for state: ${page_state}`); + // Optionally, you can call a default function if page_state does not match + // initializeDefaultPage(); // Assume you have a default page initializer + } }); \ No newline at end of file diff --git a/js/pages/00_welcome/helpers.js b/js/pages/00_welcome/helpers.js index 1313f4c..5806ef5 100644 --- a/js/pages/00_welcome/helpers.js +++ b/js/pages/00_welcome/helpers.js @@ -5,9 +5,11 @@ import { hideSideBar } from '../../components/sidebar/sidebar.js' import { hideTable } from '../../components/table/table.js' import { updateSubtitle } from '../../components/header/header.js' import { unhideWelcomeButtons } from '../../components/welcome/welcome.js' -import { loadNewInitiatives } from '../02_new_initiatives/main.js' +import { loadNewInitiatives } from '../06_new_initiatives/main.js' import { loadRevenuePage } from '../03_revenue/main.js' import { loadPersonnelPage } from '../04_personnel/main.js' +import { loadSummaryPage } from '../07_summary/main.js' +import { loadBaselineLandingPage } from '../02_baseline_landing_page/main.js' export function initializePageView(){ // page set up @@ -22,7 +24,7 @@ export function initializePageView(){ export function addLinks(){ // initialize links in buttons document.getElementById('step-initiatives').addEventListener('click', loadNewInitiatives) - document.getElementById('step-revenue').addEventListener('click', loadRevenuePage) - document.getElementById('step-personnel').addEventListener('click', loadPersonnelPage) + document.getElementById('step-revenue').addEventListener('click', loadBaselineLandingPage) + document.getElementById('step-summary').addEventListener('click', loadSummaryPage) } diff --git a/js/pages/02_baseline_landing_page/main.js b/js/pages/02_baseline_landing_page/main.js new file mode 100644 index 0000000..2c2e214 --- /dev/null +++ b/js/pages/02_baseline_landing_page/main.js @@ -0,0 +1,34 @@ +import { updatePageState } from "../../utils/storage-handlers.js"; +import { hideWelcomeButtons } from '../../components/welcome/welcome.js' +import { updateSubtitle } from '../../components/header/header.js' +import { showPrompt, updatePrompt, hidePromptButtons } from '../../components/prompt/prompt.js' +import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' +import { hideTable, showTable } from '../../components/table/table.js' +import { hideSideBar } from '../../components/sidebar/sidebar.js' +import { removeModalLink } from '../../components/modal/modal.js' +import { loadJSONIntoTable } from "../../utils/data-handlers.js"; +import { DATA_ROOT } from "../../init.js"; + +export function loadBaselineLandingPage(){ + //update page state + updatePageState('baseline-landing'); + + // prepare page view + hideWelcomeButtons(); + showPrompt(); + showNavButtons(); + hideTable('main-table'); + hideSideBar(); + removeModalLink('option1', 'main-modal'); + hidePromptButtons(); + + // update page text + updateSubtitle('Baseline Budget Request'); + // TODO: update to make dynamic + updatePrompt(`We will now ask you a series of questions about your BASELINE budget request. + At the end, we will ask you about any new initiatives (ie. supplemental requests). + Select one of your funds to begin.`); + + loadJSONIntoTable(DATA_ROOT + 'funds.json', 'main-table') + showTable('main-table'); +} \ No newline at end of file diff --git a/js/pages/03_revenue/main.js b/js/pages/03_revenue/main.js index 7761fc9..5e22969 100644 --- a/js/pages/03_revenue/main.js +++ b/js/pages/03_revenue/main.js @@ -32,6 +32,6 @@ export function loadRevenuePage() { // clicking 'confirm and continue' will also take us to the next page addPromptButtonAction('option1', nextPage); // TODO: allow user to edit revenue here - addPromptButtonAction('option2', nextPage); + // addPromptButtonAction('option2', nextPage); } \ No newline at end of file diff --git a/js/pages/04.5_OT/main.js b/js/pages/04.5_OT/main.js new file mode 100644 index 0000000..f98d8f5 --- /dev/null +++ b/js/pages/04.5_OT/main.js @@ -0,0 +1,26 @@ +import { hideWelcomeButtons } from '../../components/welcome/welcome.js' +import { updateSubtitle } from '../../components/header/header.js' +import { showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction, hidePrompt, hidePromptButtons } from '../../components/prompt/prompt.js' +import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' +import { hideTable } from '../../components/table/table.js' +import { hideSideBar } from '../../components/sidebar/sidebar.js' + +import { updatePageState } from "../../utils/storage-handlers.js"; + +export function loadOTPage(){ + //update page state + updatePageState('overtime'); + + // prepare page view + hideWelcomeButtons(); + showPrompt(); + showNavButtons(); + hideTable('main-table'); + hideSideBar(); + hidePromptButtons(); + + // update page text + updateSubtitle('Overtime Estimates'); + // TODO: update to make dynamic + updatePrompt(`This is a placeholder for the OT estimates.`); +} \ No newline at end of file diff --git a/js/pages/02_new_initiatives/helpers.js b/js/pages/06_new_initiatives/helpers.js similarity index 91% rename from js/pages/02_new_initiatives/helpers.js rename to js/pages/06_new_initiatives/helpers.js index 5215606..c36865a 100644 --- a/js/pages/02_new_initiatives/helpers.js +++ b/js/pages/06_new_initiatives/helpers.js @@ -35,7 +35,9 @@ export function setUpForm() { addForm(); addTextInput('Initiative Name:', 'Initiative Name', true); // Add required field addTextarea('Explain why this initiative is necessary and describe its potential impact.', 'Explanation', true); - addNumericInput('Roughly how additional money would this initiative require?', 'Cost', true); + addNumericInput('Estimate of ADDITONAL personnel cost?', 'Personnel Cost', true); + addNumericInput('Estimate of ADDITONAL nonpersonnel cost?', 'Non-personnel Cost', true); + addNumericInput('Estimate of total ADDITIONAL cost?', 'Total Cost', true); addSubmitButtonToForm(); // Initialize form submission to table data handleFormSubmissions(); diff --git a/js/pages/02_new_initiatives/main.js b/js/pages/06_new_initiatives/main.js similarity index 100% rename from js/pages/02_new_initiatives/main.js rename to js/pages/06_new_initiatives/main.js diff --git a/js/pages/07_summary/main.js b/js/pages/07_summary/main.js new file mode 100644 index 0000000..d3cb306 --- /dev/null +++ b/js/pages/07_summary/main.js @@ -0,0 +1,30 @@ +import { updatePageState } from "../../utils/storage-handlers.js"; +import { hideWelcomeButtons } from '../../components/welcome/welcome.js' +import { updateSubtitle } from '../../components/header/header.js' +import { showPrompt, updatePrompt, addPromptButtonAction, updatePromptButtons } from '../../components/prompt/prompt.js' +import { hideNavButtons, lastPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' +import { hideTable } from '../../components/table/table.js' +import { hideSideBar } from '../../components/sidebar/sidebar.js' +import { removeModalLink } from '../../components/modal/modal.js' +import { initializeWelcomePage } from "../00_welcome/main.js"; + +export function loadSummaryPage(){ + //update page state + updatePageState('summary'); + + // prepare page view + hideWelcomeButtons(); + showPrompt(); + showNavButtons(); + hideTable('main-table'); + hideSideBar(); + removeModalLink('option1', 'main-modal'); + updatePromptButtons('Submit', 'Go back and edit'); + hideNavButtons(); + + // update page text + updateSubtitle('Summary'); + // TODO: update to make dynamic + updatePrompt(`Placeholder for summary and any issues.`); + addPromptButtonAction('option2', initializeWelcomePage); +} \ No newline at end of file From f89b9f0056211d72c62f892f669a6cd6a33ea8fc Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Wed, 26 Jun 2024 13:52:38 -0400 Subject: [PATCH 06/30] fixed sidebar fn spelling --- js/components/sidebar/sidebar.js | 8 ++++++-- js/pages/00_welcome/helpers.js | 6 +++--- js/pages/02_baseline_landing_page/main.js | 4 ++-- js/pages/03_revenue/main.js | 4 ++-- js/pages/04.5_OT/main.js | 4 ++-- js/pages/04_personnel/helpers.js | 4 ++-- js/pages/05_nonpersonnel/helpers.js | 4 ++-- js/pages/06_new_initiatives/helpers.js | 4 ++-- js/pages/07_summary/main.js | 4 ++-- 9 files changed, 23 insertions(+), 19 deletions(-) diff --git a/js/components/sidebar/sidebar.js b/js/components/sidebar/sidebar.js index adf6ce8..58ed110 100644 --- a/js/components/sidebar/sidebar.js +++ b/js/components/sidebar/sidebar.js @@ -1,18 +1,22 @@ import { formatCurrency } from "../../utils/utils.js"; import { TARGET } from "../../init.js"; -export function hideSideBar(){ +export function hideSidebar(){ document.getElementById('sidebar-panel').style.display = 'none'; document.getElementById('main-panel').className = 'col-md-12'; } -export function showSideBar(){ +export function showSidebar(){ document.getElementById('sidebar-panel').className = 'col-md-2'; document.getElementById('sidebar-panel').style.display = 'block'; document.getElementById('main-panel').className = 'col-md-10'; addTarget(TARGET); } +export function updateSidebarTitle(new_title){ + document.getElementById('sidebar-title').textContent = new_title; +} + export function updateSidebarStat(stat_id, new_figure){ replaceSidebarStat(stat_id, new_figure); // TODO: save in memory diff --git a/js/pages/00_welcome/helpers.js b/js/pages/00_welcome/helpers.js index 5806ef5..d96648e 100644 --- a/js/pages/00_welcome/helpers.js +++ b/js/pages/00_welcome/helpers.js @@ -1,7 +1,7 @@ import { hidePrompt } from '../../components/prompt/prompt.js' import { hideNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import { hideSideBar } from '../../components/sidebar/sidebar.js' +import { hideSidebar } from '../../components/sidebar/sidebar.js' import { hideTable } from '../../components/table/table.js' import { updateSubtitle } from '../../components/header/header.js' import { unhideWelcomeButtons } from '../../components/welcome/welcome.js' @@ -14,7 +14,7 @@ import { loadBaselineLandingPage } from '../02_baseline_landing_page/main.js' export function initializePageView(){ // page set up hideTable('main-table'); - hideSideBar(); + hideSidebar(); updateSubtitle("Welcome"); unhideWelcomeButtons(); hidePrompt(); @@ -25,6 +25,6 @@ export function addLinks(){ // initialize links in buttons document.getElementById('step-initiatives').addEventListener('click', loadNewInitiatives) document.getElementById('step-revenue').addEventListener('click', loadBaselineLandingPage) - document.getElementById('step-summary').addEventListener('click', loadSummaryPage) + document.getElementById('step-finish').addEventListener('click', loadSummaryPage) } diff --git a/js/pages/02_baseline_landing_page/main.js b/js/pages/02_baseline_landing_page/main.js index 2c2e214..55eb163 100644 --- a/js/pages/02_baseline_landing_page/main.js +++ b/js/pages/02_baseline_landing_page/main.js @@ -4,7 +4,7 @@ import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, hidePromptButtons } from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { hideTable, showTable } from '../../components/table/table.js' -import { hideSideBar } from '../../components/sidebar/sidebar.js' +import { hideSidebar } from '../../components/sidebar/sidebar.js' import { removeModalLink } from '../../components/modal/modal.js' import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; @@ -18,7 +18,7 @@ export function loadBaselineLandingPage(){ showPrompt(); showNavButtons(); hideTable('main-table'); - hideSideBar(); + hideSidebar(); removeModalLink('option1', 'main-modal'); hidePromptButtons(); diff --git a/js/pages/03_revenue/main.js b/js/pages/03_revenue/main.js index 5e22969..9498b70 100644 --- a/js/pages/03_revenue/main.js +++ b/js/pages/03_revenue/main.js @@ -4,7 +4,7 @@ import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction } from '../../components/prompt/prompt.js' import { nextPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { hideTable } from '../../components/table/table.js' -import { hideSideBar } from '../../components/sidebar/sidebar.js' +import { hideSidebar } from '../../components/sidebar/sidebar.js' import { formatCurrency } from '../../utils/utils.js' import { removeModalLink } from '../../components/modal/modal.js' @@ -20,7 +20,7 @@ export function loadRevenuePage() { showPrompt(); showNavButtons(); hideTable('main-table'); - hideSideBar(); + hideSidebar(); removeModalLink('option1', 'main-modal'); // update page text diff --git a/js/pages/04.5_OT/main.js b/js/pages/04.5_OT/main.js index f98d8f5..0ec7c64 100644 --- a/js/pages/04.5_OT/main.js +++ b/js/pages/04.5_OT/main.js @@ -3,7 +3,7 @@ import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction, hidePrompt, hidePromptButtons } from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { hideTable } from '../../components/table/table.js' -import { hideSideBar } from '../../components/sidebar/sidebar.js' +import { hideSidebar } from '../../components/sidebar/sidebar.js' import { updatePageState } from "../../utils/storage-handlers.js"; @@ -16,7 +16,7 @@ export function loadOTPage(){ showPrompt(); showNavButtons(); hideTable('main-table'); - hideSideBar(); + hideSidebar(); hidePromptButtons(); // update page text diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index d61b3b8..3b07c32 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -4,7 +4,7 @@ import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { AddCostClass, addCol, addColToEnd, addEditCol, adjustTableWidth, assignClassToColumn, showTable, updateTableCell, getCellValue } from "../../components/table/table.js"; -import { incrementSidebarStat, showSideBar, updateSidebarStat } from "../../components/sidebar/sidebar.js"; +import { incrementSidebarStat, showSidebar, updateSidebarStat } from "../../components/sidebar/sidebar.js"; import { DATA_ROOT, fringe, cola, merit } from "../../init.js" import { createDropdownFromJSON } from "../../components/form/form.js"; @@ -14,7 +14,7 @@ export function preparePageView(){ hideWelcomeButtons(); showPrompt(); showNavButtons(); - showSideBar(); + showSidebar(); hidePromptButtons(); adjustTableWidth('main-table', '90%'); diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/pages/05_nonpersonnel/helpers.js index 421290b..986b975 100644 --- a/js/pages/05_nonpersonnel/helpers.js +++ b/js/pages/05_nonpersonnel/helpers.js @@ -1,7 +1,7 @@ import { hideWelcomeButtons } from "../../components/welcome/welcome.js"; import { showPrompt, hidePromptButtons, updatePrompt } from "../../components/prompt/prompt.js"; import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; -import { showSideBar, incrementSidebarStat, updateSidebarStat } from "../../components/sidebar/sidebar.js"; +import { showSidebar, incrementSidebarStat, updateSidebarStat } from "../../components/sidebar/sidebar.js"; import { AddCostClass, addColToEnd, confirm_btn, edit_btn, delete_btn, adjustTableWidth, assignClassToColumn, showTable, getCellValue } from "../../components/table/table.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; @@ -13,7 +13,7 @@ export function preparePageView(){ hideWelcomeButtons(); showPrompt(); showNavButtons(); - showSideBar(); + showSidebar(); hidePromptButtons(); adjustTableWidth('main-table', '100%'); // update page text diff --git a/js/pages/06_new_initiatives/helpers.js b/js/pages/06_new_initiatives/helpers.js index c36865a..b956967 100644 --- a/js/pages/06_new_initiatives/helpers.js +++ b/js/pages/06_new_initiatives/helpers.js @@ -6,7 +6,7 @@ import { showNavButtons, nextPage } from '../../components/nav_buttons/nav_butto import { addModalLink, updateModalTitle, clearModal, hideModal } from '../../components/modal/modal.js' import { fetchAllResponses, addTextarea, addTextInput, addNumericInput, addSubmitButtonToForm, addForm } from '../../components/form/form.js' import { adjustTableWidth, hideTable, clearTable, updateAddButtonText, addNewRow, showTable, showAddButton } from '../../components/table/table.js' -import { hideSideBar } from '../../components/sidebar/sidebar.js' +import { hideSidebar } from '../../components/sidebar/sidebar.js' export function initializePageView() { // Load text @@ -17,7 +17,7 @@ export function initializePageView() { // Prepare page view hideWelcomeButtons(); showNavButtons(); - hideSideBar(); + hideSidebar(); showPrompt(); hideTable('main-table'); } diff --git a/js/pages/07_summary/main.js b/js/pages/07_summary/main.js index d3cb306..238c41f 100644 --- a/js/pages/07_summary/main.js +++ b/js/pages/07_summary/main.js @@ -4,7 +4,7 @@ import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, addPromptButtonAction, updatePromptButtons } from '../../components/prompt/prompt.js' import { hideNavButtons, lastPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { hideTable } from '../../components/table/table.js' -import { hideSideBar } from '../../components/sidebar/sidebar.js' +import { hideSidebar } from '../../components/sidebar/sidebar.js' import { removeModalLink } from '../../components/modal/modal.js' import { initializeWelcomePage } from "../00_welcome/main.js"; @@ -17,7 +17,7 @@ export function loadSummaryPage(){ showPrompt(); showNavButtons(); hideTable('main-table'); - hideSideBar(); + hideSidebar(); removeModalLink('option1', 'main-modal'); updatePromptButtons('Submit', 'Go back and edit'); hideNavButtons(); From 674ccd86116f862675612dc1a9a4f848c01f537a Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Wed, 26 Jun 2024 16:59:11 -0400 Subject: [PATCH 07/30] cleaning up table and sidebar to be objects for easier import. still need to clear errors with functions that take params in subcomponents --- js/components/sidebar/sidebar.js | 29 ++- js/components/table/subcomponents/buttons.js | 43 ++++ js/components/table/subcomponents/cells.js | 23 ++ js/components/table/subcomponents/columns.js | 113 +++++++++ js/components/table/subcomponents/display.js | 31 +++ js/components/table/subcomponents/headers.js | 28 +++ js/components/table/subcomponents/rows.js | 32 +++ js/components/table/table.js | 239 ++----------------- js/init.js | 16 +- js/pages/00_welcome/helpers.js | 10 +- js/pages/01_upload/helpers.js | 0 js/pages/01_upload/main.js | 27 +++ js/pages/02_baseline_landing_page/main.js | 9 +- js/pages/03_revenue/main.js | 8 +- js/pages/04.5_OT/main.js | 8 +- js/pages/04_personnel/helpers.js | 32 +-- js/pages/05_nonpersonnel/helpers.js | 26 +- js/pages/06_new_initiatives/helpers.js | 4 +- js/pages/07_summary/main.js | 11 +- 19 files changed, 392 insertions(+), 297 deletions(-) create mode 100644 js/components/table/subcomponents/buttons.js create mode 100644 js/components/table/subcomponents/cells.js create mode 100644 js/components/table/subcomponents/columns.js create mode 100644 js/components/table/subcomponents/display.js create mode 100644 js/components/table/subcomponents/headers.js create mode 100644 js/components/table/subcomponents/rows.js create mode 100644 js/pages/01_upload/helpers.js create mode 100644 js/pages/01_upload/main.js diff --git a/js/components/sidebar/sidebar.js b/js/components/sidebar/sidebar.js index 58ed110..4563e69 100644 --- a/js/components/sidebar/sidebar.js +++ b/js/components/sidebar/sidebar.js @@ -1,23 +1,23 @@ import { formatCurrency } from "../../utils/utils.js"; import { TARGET } from "../../init.js"; -export function hideSidebar(){ +function hideSidebar(){ document.getElementById('sidebar-panel').style.display = 'none'; document.getElementById('main-panel').className = 'col-md-12'; } -export function showSidebar(){ +function showSidebar(){ document.getElementById('sidebar-panel').className = 'col-md-2'; document.getElementById('sidebar-panel').style.display = 'block'; document.getElementById('main-panel').className = 'col-md-10'; addTarget(TARGET); } -export function updateSidebarTitle(new_title){ +function updateSidebarTitle(new_title){ document.getElementById('sidebar-title').textContent = new_title; } -export function updateSidebarStat(stat_id, new_figure){ +function updateSidebarStat(stat_id, new_figure){ replaceSidebarStat(stat_id, new_figure); // TODO: save in memory updateTotals(); @@ -29,17 +29,17 @@ function replaceSidebarStat(stat_id, new_figure){ span.textContent = formatCurrency(new_figure); } -export function incrementSidebarStat(stat_id, new_figure){ +function incrementSidebarStat(stat_id, new_figure){ updateSidebarStat(stat_id, fetchStat(stat_id) + new_figure) } -export function fetchStat(stat_id){ +function fetchStat(stat_id){ const stat = document.querySelector(`#${stat_id} .stat`); return parseFloat(stat.getAttribute('value')) || 0; } // Function to update the display of the current and supp variables -export function updateTotals() { +function updateTotals() { // update bottom lines let supp_total = -fetchStat('supp-revenue') + fetchStat('supp-personnel') + @@ -60,6 +60,17 @@ export function updateTotals() { } } -export function addTarget(target){ +function addTarget(target){ replaceSidebarStat('target', target); -} \ No newline at end of file +} + +const Sidebar = { + hide: hideSidebar, + show: showSidebar, + updateTitle: updateSidebarTitle, + updateStat: updateSidebarStat, + incrementStat: incrementSidebarStat, + addTarget: addTarget +}; + +export default Sidebar; \ No newline at end of file diff --git a/js/components/table/subcomponents/buttons.js b/js/components/table/subcomponents/buttons.js new file mode 100644 index 0000000..2f9d4ff --- /dev/null +++ b/js/components/table/subcomponents/buttons.js @@ -0,0 +1,43 @@ +// add row button functions + +function hideAddButton(){ + document.getElementById('add-btn').style.display = 'none'; +} + +function showAddButton(){ + document.getElementById('add-btn').style.display = 'block'; +} + +function updateAddButtonText(text){ + document.getElementById('add-btn').textContent = text; +} + +const AddRowButton = { + hide: hideAddButton, + show: showAddButton, + updateText: updateAddButtonText, +}; + +// other buttons + +const Edit = { + html: '' +}; + +const Delete = { + html: '' +}; + +const Confirm = { + html: '' +}; + +export const Buttons = { + Delete: Delete, + Edit : Edit, + Confirm : Confirm, + AddRow : AddRowButton, + all : function() { Delete.html() + Edit.html() + Confirm.html() } +} + +export default Buttons; \ No newline at end of file diff --git a/js/components/table/subcomponents/cells.js b/js/components/table/subcomponents/cells.js new file mode 100644 index 0000000..13fae00 --- /dev/null +++ b/js/components/table/subcomponents/cells.js @@ -0,0 +1,23 @@ +// return cell value attribute or 0 if it does not exist +function getCellValue(row, className) { + var cell = row.querySelector(`.${className}`); + var cellValue = cell ? cell.getAttribute('value') : null; + return cellValue ? parseFloat(cellValue) : 0; +} + +function updateTableCell(row, col_class, new_value){ + const cell = row.querySelector(`.${col_class}`); + cell.setAttribute('value', new_value); + cell.textContent = formatCurrency(new_value); +} + +const Cell = { + getValue(row, className) { + return getCellValue(row, className); + }, + updateValue(row, col_class, new_value) { + updateTableCell(row, col_class, new_value); + } +}; + +export default Cell; \ No newline at end of file diff --git a/js/components/table/subcomponents/columns.js b/js/components/table/subcomponents/columns.js new file mode 100644 index 0000000..fca7b6b --- /dev/null +++ b/js/components/table/subcomponents/columns.js @@ -0,0 +1,113 @@ +import { formatCurrency } from "../../../utils/utils.js"; + +// position is index at which new column will be inserted +function addCol(position, htmlContent = '', headerTitle = '') { + // Get the table element by its ID + let table = document.getElementById('main-table'); + + // Validate position + let maxPosition = table.rows[0].cells.length; + if (position < 0 || position > maxPosition) { + console.error(`Position ${position} is out of bounds.`); + return; + } + + // Insert the header if provided + let thead = table.tHead; + if (headerTitle && thead) { + let th = document.createElement('th'); + th.innerHTML = headerTitle; // Use innerHTML to insert HTML content + thead.rows[0].insertBefore(th, thead.rows[0].cells[position]); + } + + // Insert new cells into each row of the table body + let tbody = table.tBodies[0]; + if (tbody) { + for (let i = 0; i < tbody.rows.length; i++) { + let row = tbody.rows[i]; + let td = document.createElement('td'); + td.innerHTML = htmlContent; // Use innerHTML to insert HTML content + row.insertBefore(td, row.cells[position]); + } + } +} + +function ncols(){ + const table = document.getElementById('main-table'); + // Ensure that the row exists before counting the columns + return table.rows[0].cells.length; +} + +function addColToEnd(htmlContents = [], headerTitle = ''){ + // count columns and add new column to the end + const position = ncols('main-table'); + addCol(tableId, position, htmlContents, headerTitle); +} + +function assignClassToColumn(headerName, className) { + // Get the table element by its ID + let table = document.getElementById('main-table'); + + // Find the index of the column by its header name + const thead = table.tHead; + if (!thead || thead.rows.length === 0) { + console.error('The table header is not found or has no rows.'); + return; + } + + let headerCellIndex = -1; + const headerCells = thead.rows[0].cells; // Assuming the first row contains header cells () + for (let i = 0; i < headerCells.length; i++) { + if (headerCells[i].textContent.trim() === headerName) { + headerCellIndex = i; + break; + } + } + + if (headerCellIndex === -1) { + console.error(`No header found with name "${headerName}"`); + return; + } + + // Assign the class to each cell in the specified column index within the tbody + let tbody = table.tBodies[0]; + if (tbody) { + let bodyRows = tbody.rows; + for (let row of bodyRows) { + if (row.cells[headerCellIndex]) { + row.cells[headerCellIndex].classList.add(className); + } + } + } + } + +function addCostClass(headerName){ + assignClassToColumn('main-table', headerName, 'cost'); + + // Get all the cells with the specified class name + const cells = document.querySelectorAll(`.cost`); + + cells.forEach(cell => { + // Get the current text content of the cell and assign it to 'value' attribute + if (!cell.getAttribute('value')){ + const cellValue = cell.textContent.trim(); + cell.setAttribute('value', cellValue); + + // Now format the text content like currency and replace it in the cell + const formattedCurrency = formatCurrency(parseFloat(cellValue)); + cell.textContent = formattedCurrency; + } + + }); + +} + + +const Column = { + add: addCol(position, htmlContent, headerTitle), + addAtEnd : addColToEnd(htmlContent, headerTitle), + assignClass : assignClassToColumn(headerName, className), + addCostClass : addCostClass(headerName), +}; + +export default Column; \ No newline at end of file diff --git a/js/components/table/subcomponents/display.js b/js/components/table/subcomponents/display.js new file mode 100644 index 0000000..076c85e --- /dev/null +++ b/js/components/table/subcomponents/display.js @@ -0,0 +1,31 @@ +function adjustTableWidth(width_pct){ + const table = document.getElementById('main-table'); + table.style.width = width_pct; +} + +function clearTable(){ + const table = document.getElementById('main-table'); + table.querySelector('thead').innerHTML = ''; + table.querySelector('tbody'). + innerHTML = ''; +} + +function hideTable(){ + const table = document.getElementById('main-table'); + table.style.display = 'none'; + hideAddButton(); +} + +function showTable(){ + const table = document.getElementById('main-table'); + table.style.display = 'table'; +} + +const Display = { + adjustWidth : adjustTableWidth(width_pct), + clear : clearTable, + hide : hideTable, + show : showTable +}; + +export default Display; \ No newline at end of file diff --git a/js/components/table/subcomponents/headers.js b/js/components/table/subcomponents/headers.js new file mode 100644 index 0000000..2a5e98d --- /dev/null +++ b/js/components/table/subcomponents/headers.js @@ -0,0 +1,28 @@ +function addTableHeaders(header_array){ + + // Get the table element by its ID + const table = document.getElementById('main-table'); + + // Create a table header row element + const headerRow = document.createElement('tr'); + + for (const headerText of header_array) { + + // Create a header cell element + const headerCell = document.createElement('th'); + headerCell.textContent = headerText; + + // Append the header cell to the header row + headerRow.appendChild(headerCell); + } + + // Append the header row to the table header + let thead = table.querySelector('thead'); + thead.appendChild(headerRow); +} + +const Headers = { + add: add(header_array) +}; + +export default Headers; \ No newline at end of file diff --git a/js/components/table/subcomponents/rows.js b/js/components/table/subcomponents/rows.js new file mode 100644 index 0000000..dc67ef6 --- /dev/null +++ b/js/components/table/subcomponents/rows.js @@ -0,0 +1,32 @@ +function addNewRow(data_dictionary){ + // Get the table element by its ID + const table = document.getElementById('main-table'); + + // check if header has already been added + let header_row = table.querySelector('thead tr'); + if (!header_row) { + addTableHeaders(table_id, Object.keys(data_dictionary)); + } + + // add row of data + const new_row = document.createElement('tr'); + const cell_data_array = Object.values(data_dictionary); + + for (const cell_data of cell_data_array) { + // Create new cell and add it to the row + const newCell = document.createElement('td'); + newCell.textContent = cell_data; + new_row.appendChild(newCell); + } + + // Append the new row to the table body + let tbody = table.querySelector('tbody'); + tbody.appendChild(new_row); + +} + +const Rows = { + add : addNewRow(data_dictionary) +} + +export default Rows; \ No newline at end of file diff --git a/js/components/table/table.js b/js/components/table/table.js index 65a2a75..4738f81 100644 --- a/js/components/table/table.js +++ b/js/components/table/table.js @@ -1,222 +1,17 @@ -import { formatCurrency } from "../../utils/utils.js"; - -export function addTableHeaders(table_id, header_array){ - - // Get the table element by its ID - const table = document.getElementById(table_id); - - // Create a table header row element - const headerRow = document.createElement('tr'); - - for (const headerText of header_array) { - - // Create a header cell element - const headerCell = document.createElement('th'); - headerCell.textContent = headerText; - - // Append the header cell to the header row - headerRow.appendChild(headerCell); - } - - // Append the header row to the table header - let thead = table.querySelector('thead'); - thead.appendChild(headerRow); -} - -export function addNewRow(table_id, data_dictionary){ - // Get the table element by its ID - const table = document.getElementById(table_id); - - // check if header has already been added - let header_row = table.querySelector('thead tr'); - if (!header_row) { - addTableHeaders(table_id, Object.keys(data_dictionary)); - } - - // add row of data - const new_row = document.createElement('tr'); - const cell_data_array = Object.values(data_dictionary); - - for (const cell_data of cell_data_array) { - // Create new cell and add it to the row - const newCell = document.createElement('td'); - newCell.textContent = cell_data; - new_row.appendChild(newCell); - } - - // Append the new row to the table body - let tbody = table.querySelector('tbody'); - tbody.appendChild(new_row); - -} - -export function adjustTableWidth(table_id, width_pct){ - const table = document.getElementById(table_id); - table.style.width = width_pct; -} - -export function clearTable(table_id){ - const table = document.getElementById(table_id); - table.querySelector('thead').innerHTML = ''; - table.querySelector('tbody'). - innerHTML = ''; -} - -// Add button functions -export function hideAddButton(){ - document.getElementById('add-btn').style.display = 'none'; -} - -export function showAddButton(){ - document.getElementById('add-btn').style.display = 'block'; -} - -export function updateAddButtonText(text){ - document.getElementById('add-btn').textContent = text; -} - -// Show and hide table - -export function hideTable(table_id){ - const table = document.getElementById(table_id); - table.style.display = 'none'; - hideAddButton(); -} - -export function showTable(table_id){ - const table = document.getElementById(table_id); - table.style.display = 'table'; -} - -// position is index at which new column will be inserted -export function addCol(tableId, position, htmlContent = '', headerTitle = '') { - // Get the table element by its ID - let table = document.getElementById(tableId); - - if (!table) { - console.error(`Table with ID ${tableId} not found.`); - return; - } - - // Validate position - let maxPosition = table.rows[0].cells.length; - if (position < 0 || position > maxPosition) { - console.error(`Position ${position} is out of bounds.`); - return; - } - - // Insert the header if provided - let thead = table.tHead; - if (headerTitle && thead) { - let th = document.createElement('th'); - th.innerHTML = headerTitle; // Use innerHTML to insert HTML content - thead.rows[0].insertBefore(th, thead.rows[0].cells[position]); - } - - // Insert new cells into each row of the table body - let tbody = table.tBodies[0]; - if (tbody) { - for (let i = 0; i < tbody.rows.length; i++) { - let row = tbody.rows[i]; - let td = document.createElement('td'); - td.innerHTML = htmlContent; // Use innerHTML to insert HTML content - row.insertBefore(td, row.cells[position]); - } - } -} - -function ncols(tableId){ - const table = document.getElementById(tableId); - // Ensure that the row exists before counting the columns - return table.rows[0].cells.length; -} - -export function addColToEnd(tableId, htmlContents = [], headerTitle = ''){ - // count columns and add new column to the end - const position = ncols(tableId); - addCol(tableId, position, htmlContents, headerTitle); -} - -export let confirm_btn = ''; -export let edit_btn = ''; -export let delete_btn = ''; - -// functions for editing rows -function editButton() { - return edit_btn + confirm_btn; -}; - -export function addEditCol(tableId){ - addColToEnd(tableId, editButton(), ' '); -} - -export function assignClassToColumn(tableId, headerName, className) { - // Get the table element by its ID - let table = document.getElementById(tableId); - - // Find the index of the column by its header name - const thead = table.tHead; - if (!thead || thead.rows.length === 0) { - console.error('The table header is not found or has no rows.'); - return; - } - - let headerCellIndex = -1; - const headerCells = thead.rows[0].cells; // Assuming the first row contains header cells () - for (let i = 0; i < headerCells.length; i++) { - if (headerCells[i].textContent.trim() === headerName) { - headerCellIndex = i; - break; - } - } - - if (headerCellIndex === -1) { - console.error(`No header found with name "${headerName}"`); - return; - } - - // Assign the class to each cell in the specified column index within the tbody - let tbody = table.tBodies[0]; - if (tbody) { - let bodyRows = tbody.rows; - for (let row of bodyRows) { - if (row.cells[headerCellIndex]) { - row.cells[headerCellIndex].classList.add(className); - } - } - } - } - -export function AddCostClass(tableId, headerName){ - assignClassToColumn(tableId, headerName, 'cost'); - - // Get all the cells with the specified class name - const cells = document.querySelectorAll(`.cost`); - - cells.forEach(cell => { - // Get the current text content of the cell and assign it to 'value' attribute - if (!cell.getAttribute('value')){ - const cellValue = cell.textContent.trim(); - cell.setAttribute('value', cellValue); - - // Now format the text content like currency and replace it in the cell - const formattedCurrency = formatCurrency(parseFloat(cellValue)); - cell.textContent = formattedCurrency; - } - - }); - -} - -// return cell value attribute or 0 if it does not exist -export function getCellValue(row, className) { - var cell = row.querySelector(`.${className}`); - var cellValue = cell ? cell.getAttribute('value') : null; - return cellValue ? parseFloat(cellValue) : 0; -} - -export function updateTableCell(row, col_class, new_value){ - const cell = row.querySelector(`.${col_class}`); - cell.setAttribute('value', new_value); - cell.textContent = formatCurrency(new_value); -} +import Buttons from './subcomponents/buttons.js' +import Cell from './subcomponents/cells.js' +import Columns from './subcomponents/columns.js' +import Display from './subcomponents/display.js' +import Headers from './subcomponents/headers.js' +import Rows from './subcomponents/rows.js' + +const Table = { + Buttons : Buttons, + Cell : Cell, + Columns : Columns, + Display : Display, + Headers : Headers, + Rows : Rows, +} + +export default Table; \ No newline at end of file diff --git a/js/init.js b/js/init.js index 66d6fc7..a951ea4 100644 --- a/js/init.js +++ b/js/init.js @@ -9,6 +9,7 @@ import { loadOTPage } from './pages/04.5_OT/main.js'; import { loadNonpersonnelPage } from './pages/05_nonpersonnel/main.js'; import { loadBaselineLandingPage } from './pages/02_baseline_landing_page/main.js'; import { loadSummaryPage } from './pages/07_summary/main.js'; +import { loadUploadPage } from './pages/01_upload/main.js'; // path for my laptop export let DATA_ROOT = '../../../data/law_dept_sample/' @@ -23,14 +24,17 @@ export var fringe = 0.36 export var cola = 0.02 export var merit = 0.02 -export let PAGES = {'welcome' : initializeWelcomePage, - 'baseline-landing' : loadBaselineLandingPage, +export let PAGES = { + 'welcome' : initializeWelcomePage, + 'upload' : loadUploadPage, + // 'baseline-landing' : loadBaselineLandingPage, 'revenue' : loadRevenuePage, 'personnel' : loadPersonnelPage, - 'overtime' : loadOTPage, - 'nonpersonnel' : loadNonpersonnelPage, - 'new-inits' : loadNewInitiatives, - 'summary' : loadSummaryPage } + // 'overtime' : loadOTPage, + // 'nonpersonnel' : loadNonpersonnelPage, + // 'new-inits' : loadNewInitiatives, + // 'summary' : loadSummaryPage +} document.addEventListener('DOMContentLoaded', function () { var page_state = loadPageState(); diff --git a/js/pages/00_welcome/helpers.js b/js/pages/00_welcome/helpers.js index d96648e..3ba0631 100644 --- a/js/pages/00_welcome/helpers.js +++ b/js/pages/00_welcome/helpers.js @@ -1,8 +1,8 @@ import { hidePrompt } from '../../components/prompt/prompt.js' import { hideNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import { hideSidebar } from '../../components/sidebar/sidebar.js' -import { hideTable } from '../../components/table/table.js' +import Sidebar from '../../components/sidebar/sidebar.js' +import Table from '../../components/table/table.js' import { updateSubtitle } from '../../components/header/header.js' import { unhideWelcomeButtons } from '../../components/welcome/welcome.js' import { loadNewInitiatives } from '../06_new_initiatives/main.js' @@ -10,11 +10,12 @@ import { loadRevenuePage } from '../03_revenue/main.js' import { loadPersonnelPage } from '../04_personnel/main.js' import { loadSummaryPage } from '../07_summary/main.js' import { loadBaselineLandingPage } from '../02_baseline_landing_page/main.js' +import { loadUploadPage } from '../01_upload/main.js' export function initializePageView(){ // page set up - hideTable('main-table'); - hideSidebar(); + Table.Display.hide(); + Sidebar.hide(); updateSubtitle("Welcome"); unhideWelcomeButtons(); hidePrompt(); @@ -23,6 +24,7 @@ export function initializePageView(){ export function addLinks(){ // initialize links in buttons + document.getElementById('step-upload').addEventListener('click', loadUploadPage) document.getElementById('step-initiatives').addEventListener('click', loadNewInitiatives) document.getElementById('step-revenue').addEventListener('click', loadBaselineLandingPage) document.getElementById('step-finish').addEventListener('click', loadSummaryPage) diff --git a/js/pages/01_upload/helpers.js b/js/pages/01_upload/helpers.js new file mode 100644 index 0000000..e69de29 diff --git a/js/pages/01_upload/main.js b/js/pages/01_upload/main.js new file mode 100644 index 0000000..18a6e8b --- /dev/null +++ b/js/pages/01_upload/main.js @@ -0,0 +1,27 @@ +import { updatePageState } from "../../utils/storage-handlers.js"; +import { hideWelcomeButtons } from '../../components/welcome/welcome.js' +import { updateSubtitle } from '../../components/header/header.js' +import { showPrompt, updatePrompt, addPromptButtonAction, updatePromptButtons, hidePromptButtons } from '../../components/prompt/prompt.js' +import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' +import Table from '../../components/table/table.js' +import Sidebar from '../../components/sidebar/sidebar.js' +import { initializeWelcomePage } from "../00_welcome/main.js"; + +export function loadUploadPage(){ + //update page state + updatePageState('upload'); + + // prepare page view + hideWelcomeButtons(); + showPrompt(); + hidePromptButtons(); + showNavButtons(); + Table.hide(); + Sidebar.hide(); + + // update page text + updateSubtitle('Excel Upload'); + // TODO: update to make dynamic + updatePrompt(`Placeholder for Excel Upload`); + addPromptButtonAction('option2', initializeWelcomePage); +} \ No newline at end of file diff --git a/js/pages/02_baseline_landing_page/main.js b/js/pages/02_baseline_landing_page/main.js index 55eb163..a1f01de 100644 --- a/js/pages/02_baseline_landing_page/main.js +++ b/js/pages/02_baseline_landing_page/main.js @@ -3,8 +3,8 @@ import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, hidePromptButtons } from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import { hideTable, showTable } from '../../components/table/table.js' -import { hideSidebar } from '../../components/sidebar/sidebar.js' +import Table from "../../components/table/table.js"; +import Sidebar from '../../components/sidebar/sidebar.js' import { removeModalLink } from '../../components/modal/modal.js' import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; @@ -17,8 +17,7 @@ export function loadBaselineLandingPage(){ hideWelcomeButtons(); showPrompt(); showNavButtons(); - hideTable('main-table'); - hideSidebar(); + Sidebar.hide(); removeModalLink('option1', 'main-modal'); hidePromptButtons(); @@ -30,5 +29,5 @@ export function loadBaselineLandingPage(){ Select one of your funds to begin.`); loadJSONIntoTable(DATA_ROOT + 'funds.json', 'main-table') - showTable('main-table'); + Table.Display.show(); } \ No newline at end of file diff --git a/js/pages/03_revenue/main.js b/js/pages/03_revenue/main.js index 9498b70..ae5ead9 100644 --- a/js/pages/03_revenue/main.js +++ b/js/pages/03_revenue/main.js @@ -3,8 +3,8 @@ import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction } from '../../components/prompt/prompt.js' import { nextPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import { hideTable } from '../../components/table/table.js' -import { hideSidebar } from '../../components/sidebar/sidebar.js' +import Table from '../../components/table/table.js' +import Sidebar from '../../components/sidebar/sidebar.js' import { formatCurrency } from '../../utils/utils.js' import { removeModalLink } from '../../components/modal/modal.js' @@ -19,8 +19,8 @@ export function loadRevenuePage() { hideWelcomeButtons(); showPrompt(); showNavButtons(); - hideTable('main-table'); - hideSidebar(); + Table.hide(); + Sidebar.hide(); removeModalLink('option1', 'main-modal'); // update page text diff --git a/js/pages/04.5_OT/main.js b/js/pages/04.5_OT/main.js index 0ec7c64..1d111fc 100644 --- a/js/pages/04.5_OT/main.js +++ b/js/pages/04.5_OT/main.js @@ -2,8 +2,8 @@ import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction, hidePrompt, hidePromptButtons } from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import { hideTable } from '../../components/table/table.js' -import { hideSidebar } from '../../components/sidebar/sidebar.js' +import Table from '../../components/table/table.js' +import Sidebar from '../../components/sidebar/sidebar.js' import { updatePageState } from "../../utils/storage-handlers.js"; @@ -15,8 +15,8 @@ export function loadOTPage(){ hideWelcomeButtons(); showPrompt(); showNavButtons(); - hideTable('main-table'); - hideSidebar(); + Sidebar.hide(); + Table.Display.hide(); hidePromptButtons(); // update page text diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index 3b07c32..d53ce42 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -3,8 +3,8 @@ import { hidePromptButtons, showPrompt, updatePrompt } from "../../components/pr import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; -import { AddCostClass, addCol, addColToEnd, addEditCol, adjustTableWidth, assignClassToColumn, showTable, updateTableCell, getCellValue } from "../../components/table/table.js"; -import { incrementSidebarStat, showSidebar, updateSidebarStat } from "../../components/sidebar/sidebar.js"; +import Table from '../../components/table/table.js' +import Sidebar from "../../components/sidebar/sidebar.js"; import { DATA_ROOT, fringe, cola, merit } from "../../init.js" import { createDropdownFromJSON } from "../../components/form/form.js"; @@ -27,11 +27,11 @@ export async function initializePersonnelTable(){ // load table data from json await loadJSONIntoTable(DATA_ROOT + 'personnel_data.json', 'main-table'); //after table is loaded, fill it - showTable('main-table'); - addCol('main-table', 3, '', 'Service'); - addColToEnd('main-table', '0', 'Total Cost (Baseline)'); - addColToEnd('main-table', '0', 'Total Cost (Supplementary)'); - addEditCol('main-table'); + Table.Display.show(); + Table.Columns.add(3, '', 'Service'); + Table.Columns.addAtEnd('0', 'Total Cost (Baseline)'); + Table.Columns.addAtEnd( '0', 'Total Cost (Supplementary)'); + Table.Columns.addAtEnd(Table.Buttons.Edit.html + Table.Buttons.Confirm.html, ' '); // assign cost classes assignClassToColumn('main-table', 'Current Average Salary', 'avg-salary'); AddCostClass('main-table', 'Current Average Salary'); @@ -149,26 +149,26 @@ function calculateTotalCost(ftes, avg_salary, fringe, cola, merit){ // update sidebar and also cost totals when the FTEs are edited function updateDisplayandTotals(){ // initialize - updateSidebarStat('baseline-personnel', 0); - updateSidebarStat('supp-personnel', 0); + Sidebar.updateStat('baseline-personnel', 0); + Sidebar.updateStat('supp-personnel', 0); // calculate for each row let rows = document.getElementsByTagName('tr'); for (let i = 1; i < rows.length; i++){ // fetch values for calculations - let avg_salary = getCellValue(rows[i], 'avg-salary'); - let baseline_ftes = getCellValue(rows[i], 'baseline-ftes'); - let supp_ftes = getCellValue(rows[i], 'supp-ftes'); + let avg_salary = Table.Cell.getValue(rows[i], 'avg-salary'); + let baseline_ftes = Table.Cell.getValue(rows[i], 'baseline-ftes'); + let supp_ftes = Table.Cell.getValue(rows[i], 'supp-ftes'); // calcuate #FTEs x average salary + COLA adjustments + merit adjustments + fringe let total_baseline_cost = calculateTotalCost(baseline_ftes, avg_salary, fringe, cola, merit); let total_supp_cost = calculateTotalCost(supp_ftes, avg_salary, fringe, cola, merit); // update counters - incrementSidebarStat('baseline-personnel', total_baseline_cost); - incrementSidebarStat('supp-personnel', total_supp_cost); + Sidebar.incrementStat('baseline-personnel', total_baseline_cost); + Sidebar.incrementStat('supp-personnel', total_supp_cost); // update totals in table - updateTableCell(rows[i], 'total-baseline', total_baseline_cost); - updateTableCell(rows[i], 'total-supp', total_supp_cost); + Table.Cell.updateValue(rows[i], 'total-baseline', total_baseline_cost); + Table.Cell.updateValue(rows[i], 'total-supp', total_supp_cost); } } diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/pages/05_nonpersonnel/helpers.js index 986b975..c740cfa 100644 --- a/js/pages/05_nonpersonnel/helpers.js +++ b/js/pages/05_nonpersonnel/helpers.js @@ -1,8 +1,8 @@ import { hideWelcomeButtons } from "../../components/welcome/welcome.js"; import { showPrompt, hidePromptButtons, updatePrompt } from "../../components/prompt/prompt.js"; import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; -import { showSidebar, incrementSidebarStat, updateSidebarStat } from "../../components/sidebar/sidebar.js"; -import { AddCostClass, addColToEnd, confirm_btn, edit_btn, delete_btn, adjustTableWidth, assignClassToColumn, showTable, getCellValue } from "../../components/table/table.js"; +import Sidebar from "../../components/sidebar/sidebar.js"; +import Table from "../../components/table/table.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; @@ -13,9 +13,9 @@ export function preparePageView(){ hideWelcomeButtons(); showPrompt(); showNavButtons(); - showSidebar(); + Sidebar.show(); hidePromptButtons(); - adjustTableWidth('main-table', '100%'); + Table.Display.adjustWidth('100%'); // update page text updateSubtitle('Non-Personnel'); updatePrompt('Select an action item for each non-personnel line item from last year.'); @@ -26,21 +26,11 @@ export async function initializeNonpersonnelTable(){ // load table data from json await loadJSONIntoTable(DATA_ROOT + 'nonpersonnel_data.json', 'main-table'); //after table is loaded, fill it - showTable('main-table'); - addColToEnd('main-table', `${delete_btn} ${edit_btn} ${confirm_btn}`, "Select Action"); + Table.Display.show(); + Table.Columns.addAtEnd(Table.Buttons.all(), "Select Action"); // assign cost classes - assignClassToColumn('main-table', 'Request Total', 'request'); - AddCostClass('main-table', 'Request Total'); - // assignClassToColumn('main-table', 'Total Cost (Baseline)', 'total-baseline'); - // AddCostClass('main-table', 'Total Cost (Baseline)'); - // assignClassToColumn('main-table', 'Total Cost (Supplementary)', 'total-supp'); - // AddCostClass('main-table', 'Total Cost (Supplementary)'); - // // assign other classes - // assignClassToColumn('main-table', 'Job Name', 'job-name'); - // assignClassToColumn('main-table', 'Baseline FTEs', 'baseline-ftes'); - // assignClassToColumn('main-table', 'Supplemental FTEs', 'supp-ftes'); - // assignClassToColumn('main-table', 'Service', 'service'); - // manage edit buttons + Table.Columns.assignClass('Request Total', 'request'); + Table.Columns.addCostClass('Request Total'); handleRowEdit(['request']); } diff --git a/js/pages/06_new_initiatives/helpers.js b/js/pages/06_new_initiatives/helpers.js index b956967..d839a30 100644 --- a/js/pages/06_new_initiatives/helpers.js +++ b/js/pages/06_new_initiatives/helpers.js @@ -5,8 +5,8 @@ import { hidePrompt, showPrompt, updatePrompt, updatePromptButtons, addPromptBut import { showNavButtons, nextPage } from '../../components/nav_buttons/nav_buttons.js' import { addModalLink, updateModalTitle, clearModal, hideModal } from '../../components/modal/modal.js' import { fetchAllResponses, addTextarea, addTextInput, addNumericInput, addSubmitButtonToForm, addForm } from '../../components/form/form.js' -import { adjustTableWidth, hideTable, clearTable, updateAddButtonText, addNewRow, showTable, showAddButton } from '../../components/table/table.js' -import { hideSidebar } from '../../components/sidebar/sidebar.js' +import Sidebar from '../../components/sidebar/sidebar.js' +import Table from '../../components/table/table.js' export function initializePageView() { // Load text diff --git a/js/pages/07_summary/main.js b/js/pages/07_summary/main.js index 238c41f..8f285d3 100644 --- a/js/pages/07_summary/main.js +++ b/js/pages/07_summary/main.js @@ -3,9 +3,8 @@ import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, addPromptButtonAction, updatePromptButtons } from '../../components/prompt/prompt.js' import { hideNavButtons, lastPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import { hideTable } from '../../components/table/table.js' -import { hideSidebar } from '../../components/sidebar/sidebar.js' -import { removeModalLink } from '../../components/modal/modal.js' +import Sidebar from '../../components/sidebar/sidebar.js' +import Table from "../../components/table/table.js"; import { initializeWelcomePage } from "../00_welcome/main.js"; export function loadSummaryPage(){ @@ -15,10 +14,8 @@ export function loadSummaryPage(){ // prepare page view hideWelcomeButtons(); showPrompt(); - showNavButtons(); - hideTable('main-table'); - hideSidebar(); - removeModalLink('option1', 'main-modal'); + Table.Display.hide(); + Sidebar.hide(); updatePromptButtons('Submit', 'Go back and edit'); hideNavButtons(); From 0ed3bc84103d66b84a46f79a45a50d7f629c6860 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Thu, 27 Jun 2024 14:05:53 -0400 Subject: [PATCH 08/30] refactoring table functions and moving personnel and nonpersonnel table functions to appropriate components --- js/components/table/subcomponents/buttons.js | 97 ++++++++++++++--- js/components/table/subcomponents/cells.js | 6 +- js/components/table/subcomponents/columns.js | 29 ++++- js/components/table/subcomponents/display.js | 31 ------ js/components/table/subcomponents/headers.js | 4 +- js/components/table/subcomponents/rows.js | 20 +++- js/components/table/table.js | 32 +++++- js/init.js | 10 +- js/pages/00_welcome/helpers.js | 2 +- js/pages/02_baseline_landing_page/main.js | 2 +- js/pages/04.5_OT/main.js | 2 +- js/pages/04_personnel/helpers.js | 109 ++++--------------- js/pages/05_nonpersonnel/helpers.js | 6 +- js/pages/07_summary/main.js | 2 +- js/utils/data-handlers.js | 4 +- 15 files changed, 194 insertions(+), 162 deletions(-) delete mode 100644 js/components/table/subcomponents/display.js diff --git a/js/components/table/subcomponents/buttons.js b/js/components/table/subcomponents/buttons.js index 2f9d4ff..5f9097c 100644 --- a/js/components/table/subcomponents/buttons.js +++ b/js/components/table/subcomponents/buttons.js @@ -1,43 +1,104 @@ -// add row button functions - -function hideAddButton(){ - document.getElementById('add-btn').style.display = 'none'; +function hideButton(id){ + return function() { + document.getElementById(id).style.display = 'none'; + } } -function showAddButton(){ - document.getElementById('add-btn').style.display = 'block'; +function showButton(id){ + return function(){ + document.getElementById(id).style.display = ''; + } } -function updateAddButtonText(text){ - document.getElementById('add-btn').textContent = text; +function updateButtonText(id, text){ + document.getElementById(id).textContent = text; } -const AddRowButton = { - hide: hideAddButton, - show: showAddButton, - updateText: updateAddButtonText, +// button for adding a row + +const AddRow = { + hide: hideButton('btn-add'), + show: showButton('btn-add'), + updateText: function(text){ + updateButtonText('btn-add', text); + } }; -// other buttons +// EDIT button + +function handleRowEdit(makeRowEditable){ + // attach an event listener to each edit button in every row + var editButtons = document.getElementsByClassName('btn-edit'); + for (var i = 0; i < editButtons.length; i++) { + editButtons[i].addEventListener('click', async function(event) { + // Determine what was clicked on within the table + var rowToEdit = event.target.closest('tr'); + // mark row as being edited + rowToEdit.classList.add('active-editing'); + + // turn relevant entries into textboxes + makeRowEditable(); + + // hide edit buttons + var editButtons = document.getElementsByClassName('btn-edit'); + for (var i = 0; i < editButtons.length; i++) { + editButtons[i].style.display = 'none'; + } + initializeConfirmButton(rowToEdit); + + }); + }; +} + +// Confirm button + +function initializeConfirmButton(updateCallback){ + // show confirm button + showButton('btn-confirm'); + // get element and add listener for click + var rowToEdit = document.querySelector('.active-editing') + const confirm_btn = rowToEdit.querySelector(".btn-confirm");; + confirm_btn.addEventListener('click', function(event){; + // save row edits + Table.Rows.saveEdits(rowToEdit); + // update values in sidebar + updateCallback(); + // make row no longer green + rowToEdit.classList.remove('active-editing'); + // show edit buttons and hide confirm buttons + Edit.show(); + Confirm.hide(); + }); +} const Edit = { - html: '' + html: '', + hide: hideButton('btn-edit'), + show: showButton('btn-edit'), + init : function(makeRowEditable){ + handleRowEdit(makeRowEditable) + } }; const Delete = { - html: '' + html: '', + hide: hideButton('btn-delete'), + show: showButton('btn-delete') }; const Confirm = { - html: '' + html: '', + hide: hideButton('btn-confirm'), + show: showButton('btn-confirm'), + init : function(callback){ initializeConfirmButton(callback) } }; export const Buttons = { Delete: Delete, Edit : Edit, Confirm : Confirm, - AddRow : AddRowButton, - all : function() { Delete.html() + Edit.html() + Confirm.html() } + edit_confirm_btns : Edit.html + Confirm.html , + all_btns : Delete.html + Edit.html + Confirm.html } export default Buttons; \ No newline at end of file diff --git a/js/components/table/subcomponents/cells.js b/js/components/table/subcomponents/cells.js index 13fae00..137270c 100644 --- a/js/components/table/subcomponents/cells.js +++ b/js/components/table/subcomponents/cells.js @@ -1,3 +1,5 @@ +import { formatCurrency } from "../../../utils/utils.js"; + // return cell value attribute or 0 if it does not exist function getCellValue(row, className) { var cell = row.querySelector(`.${className}`); @@ -12,10 +14,10 @@ function updateTableCell(row, col_class, new_value){ } const Cell = { - getValue(row, className) { + getValue: function(row, className) { return getCellValue(row, className); }, - updateValue(row, col_class, new_value) { + updateValue: function(row, col_class, new_value) { updateTableCell(row, col_class, new_value); } }; diff --git a/js/components/table/subcomponents/columns.js b/js/components/table/subcomponents/columns.js index fca7b6b..fbbde3f 100644 --- a/js/components/table/subcomponents/columns.js +++ b/js/components/table/subcomponents/columns.js @@ -41,7 +41,7 @@ function ncols(){ function addColToEnd(htmlContents = [], headerTitle = ''){ // count columns and add new column to the end const position = ncols('main-table'); - addCol(tableId, position, htmlContents, headerTitle); + addCol(position, htmlContents, headerTitle); } function assignClassToColumn(headerName, className) { @@ -82,7 +82,7 @@ function assignClassToColumn(headerName, className) { } function addCostClass(headerName){ - assignClassToColumn('main-table', headerName, 'cost'); + assignClassToColumn( headerName, 'cost'); // Get all the cells with the specified class name const cells = document.querySelectorAll(`.cost`); @@ -102,12 +102,29 @@ function addCostClass(headerName){ } +function assignColumnClasses(columnDefinitions) { + columnDefinitions.forEach(column => { + // Assign class to column + assignClassToColumn(column.title, column.className); + + // If the column is a cost column, add the specific cost class + if (column.isCost) { + addCostClass(column.title); + } + }); +} + const Column = { - add: addCol(position, htmlContent, headerTitle), - addAtEnd : addColToEnd(htmlContent, headerTitle), - assignClass : assignClassToColumn(headerName, className), - addCostClass : addCostClass(headerName), + add: function(position, htmlContent, headerTitle) { + return addCol(position, htmlContent, headerTitle); + }, + addAtEnd: function(htmlContent, headerTitle) { + return addColToEnd(htmlContent, headerTitle); + }, + assignClasses: function(column_definitions) { + return assignColumnClasses(column_definitions); + } }; export default Column; \ No newline at end of file diff --git a/js/components/table/subcomponents/display.js b/js/components/table/subcomponents/display.js deleted file mode 100644 index 076c85e..0000000 --- a/js/components/table/subcomponents/display.js +++ /dev/null @@ -1,31 +0,0 @@ -function adjustTableWidth(width_pct){ - const table = document.getElementById('main-table'); - table.style.width = width_pct; -} - -function clearTable(){ - const table = document.getElementById('main-table'); - table.querySelector('thead').innerHTML = ''; - table.querySelector('tbody'). - innerHTML = ''; -} - -function hideTable(){ - const table = document.getElementById('main-table'); - table.style.display = 'none'; - hideAddButton(); -} - -function showTable(){ - const table = document.getElementById('main-table'); - table.style.display = 'table'; -} - -const Display = { - adjustWidth : adjustTableWidth(width_pct), - clear : clearTable, - hide : hideTable, - show : showTable -}; - -export default Display; \ No newline at end of file diff --git a/js/components/table/subcomponents/headers.js b/js/components/table/subcomponents/headers.js index 2a5e98d..8e12194 100644 --- a/js/components/table/subcomponents/headers.js +++ b/js/components/table/subcomponents/headers.js @@ -22,7 +22,9 @@ function addTableHeaders(header_array){ } const Headers = { - add: add(header_array) + add: function(header_array){ + addTableHeaders(header_array) + } }; export default Headers; \ No newline at end of file diff --git a/js/components/table/subcomponents/rows.js b/js/components/table/subcomponents/rows.js index dc67ef6..827563e 100644 --- a/js/components/table/subcomponents/rows.js +++ b/js/components/table/subcomponents/rows.js @@ -22,11 +22,29 @@ function addNewRow(data_dictionary){ // Append the new row to the table body let tbody = table.querySelector('tbody'); tbody.appendChild(new_row); +} +function saveRowEdits(row){ + var cells = rowToEdit.querySelectorAll('td') + cells.forEach( cell => { + // save dropdown values + if (cell.querySelector('select')){ + cell.textContent = serviceSelector.value; + } else if (cell.querySelector('input')) { + var enteredValue = textbox.value; + cell.textContent = enteredValue; + cell.setAttribute('value', enteredValue); + } + }) } const Rows = { - add : addNewRow(data_dictionary) + add : function(data_dictionary){ + addNewRow(data_dictionary) + }, + saveEdits : function(row){ + saveRowEdits(row) + } } export default Rows; \ No newline at end of file diff --git a/js/components/table/table.js b/js/components/table/table.js index 4738f81..6a61d0c 100644 --- a/js/components/table/table.js +++ b/js/components/table/table.js @@ -1,17 +1,45 @@ import Buttons from './subcomponents/buttons.js' import Cell from './subcomponents/cells.js' import Columns from './subcomponents/columns.js' -import Display from './subcomponents/display.js' import Headers from './subcomponents/headers.js' import Rows from './subcomponents/rows.js' +function adjustTableWidth(width_pct){ + const table = document.getElementById('main-table'); + table.style.width = width_pct; +} + +function clearTable(){ + const table = document.getElementById('main-table'); + table.querySelector('thead').innerHTML = ''; + table.querySelector('tbody'). + innerHTML = ''; +} + +function showTable(){ + const table = document.getElementById('main-table'); + table.style.display = 'table'; +} + +function hideTable(){ + const table = document.getElementById('main-table'); + table.style.display = 'none'; + Table.Buttons.AddRow.hide(); +} + const Table = { Buttons : Buttons, Cell : Cell, Columns : Columns, - Display : Display, Headers : Headers, Rows : Rows, + // functions + adjustWidth : function(width_pct){ + adjustTableWidth(width_pct) + }, + clear : clearTable, + hide : hideTable, + show : showTable } export default Table; \ No newline at end of file diff --git a/js/init.js b/js/init.js index a951ea4..941fd77 100644 --- a/js/init.js +++ b/js/init.js @@ -27,13 +27,13 @@ export var merit = 0.02 export let PAGES = { 'welcome' : initializeWelcomePage, 'upload' : loadUploadPage, - // 'baseline-landing' : loadBaselineLandingPage, + 'baseline-landing' : loadBaselineLandingPage, 'revenue' : loadRevenuePage, 'personnel' : loadPersonnelPage, - // 'overtime' : loadOTPage, - // 'nonpersonnel' : loadNonpersonnelPage, - // 'new-inits' : loadNewInitiatives, - // 'summary' : loadSummaryPage + 'overtime' : loadOTPage, + 'nonpersonnel' : loadNonpersonnelPage, + 'new-inits' : loadNewInitiatives, + 'summary' : loadSummaryPage } document.addEventListener('DOMContentLoaded', function () { diff --git a/js/pages/00_welcome/helpers.js b/js/pages/00_welcome/helpers.js index 3ba0631..84f47e5 100644 --- a/js/pages/00_welcome/helpers.js +++ b/js/pages/00_welcome/helpers.js @@ -14,7 +14,7 @@ import { loadUploadPage } from '../01_upload/main.js' export function initializePageView(){ // page set up - Table.Display.hide(); + Table.hide(); Sidebar.hide(); updateSubtitle("Welcome"); unhideWelcomeButtons(); diff --git a/js/pages/02_baseline_landing_page/main.js b/js/pages/02_baseline_landing_page/main.js index a1f01de..2b60dbe 100644 --- a/js/pages/02_baseline_landing_page/main.js +++ b/js/pages/02_baseline_landing_page/main.js @@ -29,5 +29,5 @@ export function loadBaselineLandingPage(){ Select one of your funds to begin.`); loadJSONIntoTable(DATA_ROOT + 'funds.json', 'main-table') - Table.Display.show(); + Table.show(); } \ No newline at end of file diff --git a/js/pages/04.5_OT/main.js b/js/pages/04.5_OT/main.js index 1d111fc..170c2bb 100644 --- a/js/pages/04.5_OT/main.js +++ b/js/pages/04.5_OT/main.js @@ -16,7 +16,7 @@ export function loadOTPage(){ showPrompt(); showNavButtons(); Sidebar.hide(); - Table.Display.hide(); + Table.hide(); hidePromptButtons(); // update page text diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index d53ce42..0c8b7a4 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -9,69 +9,48 @@ import { DATA_ROOT, fringe, cola, merit } from "../../init.js" import { createDropdownFromJSON } from "../../components/form/form.js"; +const personnelColumns = [ + { title: 'Job Name', className: 'job-name' }, + { title: 'Baseline FTEs', className: 'baseline-ftes' }, + { title: 'Supplemental FTEs', className: 'supp-ftes' }, + { title: 'Service', className: 'service' }, + { title: 'Total Cost (Baseline)', className: 'total-baseline', isCost: true }, + { title: 'Total Cost (Supplementary)', className: 'total-supp', isCost: true }, +]; + export function preparePageView(){ // prepare page view hideWelcomeButtons(); showPrompt(); showNavButtons(); - showSidebar(); + Sidebar.show(); hidePromptButtons(); - adjustTableWidth('main-table', '90%'); + Table.adjustWidth('90%'); // update page text updateSubtitle('Personnel'); updatePrompt('For each job in your department, select the service and request the number of baseline and supplemental FTEs.'); } +function personnelRowOnEdit(){ + createEditableCell('baseline-ftes'); + createEditableCell('supp-ftes'); + createSelectCell('service'); +} + export async function initializePersonnelTable(){ // load table data from json - await loadJSONIntoTable(DATA_ROOT + 'personnel_data.json', 'main-table'); + await loadJSONIntoTable(DATA_ROOT + 'personnel_data.json'); //after table is loaded, fill it - Table.Display.show(); + Table.show(); Table.Columns.add(3, '', 'Service'); Table.Columns.addAtEnd('0', 'Total Cost (Baseline)'); Table.Columns.addAtEnd( '0', 'Total Cost (Supplementary)'); - Table.Columns.addAtEnd(Table.Buttons.Edit.html + Table.Buttons.Confirm.html, ' '); + Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, ' '); // assign cost classes - assignClassToColumn('main-table', 'Current Average Salary', 'avg-salary'); - AddCostClass('main-table', 'Current Average Salary'); - assignClassToColumn('main-table', 'Total Cost (Baseline)', 'total-baseline'); - AddCostClass('main-table', 'Total Cost (Baseline)'); - assignClassToColumn('main-table', 'Total Cost (Supplementary)', 'total-supp'); - AddCostClass('main-table', 'Total Cost (Supplementary)'); - // assign other classes - assignClassToColumn('main-table', 'Job Name', 'job-name'); - assignClassToColumn('main-table', 'Baseline FTEs', 'baseline-ftes'); - assignClassToColumn('main-table', 'Supplemental FTEs', 'supp-ftes'); - assignClassToColumn('main-table', 'Service', 'service'); - // manage edit buttons - handleRowEdit(); -} - -export function handleRowEdit(){ - // attach an event listener to each edit button in every row - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].addEventListener('click', async function(event) { - // Determine what was clicked on within the table - var rowToEdit = event.target.closest('tr'); - // mark row as being edited - rowToEdit.classList.add('active-editing'); - - // turn relevant entries into textboxes - createEditableCell('baseline-ftes'); - createEditableCell('supp-ftes'); - createSelectCell('service'); - - // hide edit buttons - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].style.display = 'none'; - } - - initializeConfirmButton(rowToEdit); - }); - }; + Table.Columns.assignClasses(personnelColumns) + // activate edit buttons + Table.Buttons.Edit.init(personnelRowOnEdit); } @@ -97,48 +76,6 @@ async function createSelectCell(cellClass){ // Clear the current content and append the textbox to the cell cell.innerHTML = ''; cell.appendChild(serviceDropdown); -} - -function initializeConfirmButton(rowToEdit){ - // get element and add listener for click - const confirm_btn = rowToEdit.querySelector(".btn-confirm"); - // show confirm button - confirm_btn.style.display = 'block'; - confirm_btn.addEventListener('click', function(event){ - // get current row - const rowToEdit = event.target.closest('tr'); - - // set service selection - const serviceSelector = rowToEdit.querySelector('select'); - if (serviceSelector){ - var cell = serviceSelector.closest('td'); - cell.textContent = serviceSelector.value; - } - - var textboxes = rowToEdit.querySelectorAll('input'); - // save all text in textboxes - textboxes.forEach( textbox => { - var enteredValue = textbox.value; - var cell = textbox.parentElement; - cell.textContent = enteredValue; - cell.setAttribute('value', enteredValue); - }) - - // update values in sidebar - updateDisplayandTotals(); - - // make row no longer green - rowToEdit.classList.remove('active-editing'); - - // show edit buttons - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].style.display = 'block'; - } - - // hide confirm button - confirm_btn.style.display = 'none'; - }); } diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/pages/05_nonpersonnel/helpers.js index c740cfa..5d5c4e6 100644 --- a/js/pages/05_nonpersonnel/helpers.js +++ b/js/pages/05_nonpersonnel/helpers.js @@ -15,7 +15,7 @@ export function preparePageView(){ showNavButtons(); Sidebar.show(); hidePromptButtons(); - Table.Display.adjustWidth('100%'); + Table.adjustWidth('100%'); // update page text updateSubtitle('Non-Personnel'); updatePrompt('Select an action item for each non-personnel line item from last year.'); @@ -26,7 +26,7 @@ export async function initializeNonpersonnelTable(){ // load table data from json await loadJSONIntoTable(DATA_ROOT + 'nonpersonnel_data.json', 'main-table'); //after table is loaded, fill it - Table.Display.show(); + Table.show(); Table.Columns.addAtEnd(Table.Buttons.all(), "Select Action"); // assign cost classes Table.Columns.assignClass('Request Total', 'request'); @@ -60,8 +60,6 @@ export function handleRowEdit(editable_col_classes){ }; } -// on edit, hide delete button. On - function createEditableCell(cellClass, attribute = 'value'){ // get cell const cell = document.querySelector(`.active-editing td.${cellClass}`); diff --git a/js/pages/07_summary/main.js b/js/pages/07_summary/main.js index 8f285d3..69f0f39 100644 --- a/js/pages/07_summary/main.js +++ b/js/pages/07_summary/main.js @@ -14,7 +14,7 @@ export function loadSummaryPage(){ // prepare page view hideWelcomeButtons(); showPrompt(); - Table.Display.hide(); + Table.hide(); Sidebar.hide(); updatePromptButtons('Submit', 'Go back and edit'); hideNavButtons(); diff --git a/js/utils/data-handlers.js b/js/utils/data-handlers.js index 4d00f1f..525dc1b 100644 --- a/js/utils/data-handlers.js +++ b/js/utils/data-handlers.js @@ -1,4 +1,4 @@ -export function loadJSONIntoTable(jsonFilePath, tableId) { +export function loadJSONIntoTable(jsonFilePath) { return fetch(jsonFilePath) .then(response => { if (!response.ok) { @@ -8,7 +8,7 @@ export function loadJSONIntoTable(jsonFilePath, tableId) { }) .then(data => { if(Array.isArray(data)) { - const table = document.getElementById(tableId); + const table = document.getElementById('main-table'); const thead = table.querySelector('thead'); const tbody = table.querySelector('tbody'); From 3b308fb1b275c70d9fa7e33819bba2fb0502432b Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Thu, 27 Jun 2024 15:34:37 -0400 Subject: [PATCH 09/30] refactored 04_personnel helpers --- js/components/table/subcomponents/buttons.js | 48 +++++++++++--------- js/components/table/subcomponents/cells.js | 32 ++++++++++++- js/components/table/subcomponents/rows.js | 4 +- js/pages/04_personnel/helpers.js | 42 ++++------------- 4 files changed, 68 insertions(+), 58 deletions(-) diff --git a/js/components/table/subcomponents/buttons.js b/js/components/table/subcomponents/buttons.js index 5f9097c..3fc500a 100644 --- a/js/components/table/subcomponents/buttons.js +++ b/js/components/table/subcomponents/buttons.js @@ -1,17 +1,25 @@ -function hideButton(id){ +import Rows from './rows.js' + +function hideButton(className){ return function() { - document.getElementById(id).style.display = 'none'; + var buttons = document.getElementsByClassName(className); + for (var i = 0; i < buttons.length; i++) { + buttons[i].style.display = 'none'; + } } } -function showButton(id){ - return function(){ - document.getElementById(id).style.display = ''; +function showButton(className){ + return function() { + var buttons = document.getElementsByClassName(className); + for (var i = 0; i < buttons.length; i++) { + buttons[i].style.display = ''; + } } } -function updateButtonText(id, text){ - document.getElementById(id).textContent = text; +function updateButtonText(className, text){ + document.querySelector(`.${className}`).textContent = text; } // button for adding a row @@ -26,7 +34,7 @@ const AddRow = { // EDIT button -function handleRowEdit(makeRowEditable){ +function handleRowEdit(makeRowEditable, updateCallback){ // attach an event listener to each edit button in every row var editButtons = document.getElementsByClassName('btn-edit'); for (var i = 0; i < editButtons.length; i++) { @@ -40,11 +48,8 @@ function handleRowEdit(makeRowEditable){ makeRowEditable(); // hide edit buttons - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].style.display = 'none'; - } - initializeConfirmButton(rowToEdit); + Edit.hide(); + initializeConfirmButton(updateCallback); }); }; @@ -53,14 +58,14 @@ function handleRowEdit(makeRowEditable){ // Confirm button function initializeConfirmButton(updateCallback){ - // show confirm button - showButton('btn-confirm'); // get element and add listener for click - var rowToEdit = document.querySelector('.active-editing') - const confirm_btn = rowToEdit.querySelector(".btn-confirm");; + var rowToEdit = document.querySelector('.active-editing'); + const confirm_btn = rowToEdit.querySelector(".btn-confirm"); + // show the row's confirm button + confirm_btn.style.display = 'block'; confirm_btn.addEventListener('click', function(event){; // save row edits - Table.Rows.saveEdits(rowToEdit); + Rows.saveEdits(rowToEdit); // update values in sidebar updateCallback(); // make row no longer green @@ -75,8 +80,8 @@ const Edit = { html: '', hide: hideButton('btn-edit'), show: showButton('btn-edit'), - init : function(makeRowEditable){ - handleRowEdit(makeRowEditable) + init : function(makeRowEditable, updateCallback){ + handleRowEdit(makeRowEditable, updateCallback) } }; @@ -89,8 +94,7 @@ const Delete = { const Confirm = { html: '', hide: hideButton('btn-confirm'), - show: showButton('btn-confirm'), - init : function(callback){ initializeConfirmButton(callback) } + show: showButton('btn-confirm') }; export const Buttons = { diff --git a/js/components/table/subcomponents/cells.js b/js/components/table/subcomponents/cells.js index 137270c..5837408 100644 --- a/js/components/table/subcomponents/cells.js +++ b/js/components/table/subcomponents/cells.js @@ -1,4 +1,5 @@ import { formatCurrency } from "../../../utils/utils.js"; +import { createDropdownFromJSON } from "../../form/form.js"; // return cell value attribute or 0 if it does not exist function getCellValue(row, className) { @@ -13,13 +14,42 @@ function updateTableCell(row, col_class, new_value){ cell.textContent = formatCurrency(new_value); } +function createEditableCell(cellClass){ + // get cell + const cell = document.querySelector(`.active-editing td.${cellClass}`); + // Create an input element to edit the value + var textbox = document.createElement('input'); + textbox.type = 'text'; + textbox.value = cell.textContent; + // Clear the current content and append the textbox to the cell + cell.innerHTML = ''; + cell.appendChild(textbox); +} + +async function createSelectCell(cellClass, json_filepath){ + // get cell + const cell = document.querySelector(`.active-editing td.${cellClass}`); + // add service dropdown + const serviceDropdown = await createDropdownFromJSON(json_filepath); + serviceDropdown.value = cell.textContent; + // Clear the current content and append the textbox to the cell + cell.innerHTML = ''; + cell.appendChild(serviceDropdown); +} + const Cell = { getValue: function(row, className) { return getCellValue(row, className); }, updateValue: function(row, col_class, new_value) { updateTableCell(row, col_class, new_value); - } + }, + createTextbox : function(className) { + createEditableCell(className) + }, + createDropdown : function(className, json_filepath){ + createSelectCell(className, json_filepath); + }, }; export default Cell; \ No newline at end of file diff --git a/js/components/table/subcomponents/rows.js b/js/components/table/subcomponents/rows.js index 827563e..d5a3569 100644 --- a/js/components/table/subcomponents/rows.js +++ b/js/components/table/subcomponents/rows.js @@ -25,12 +25,14 @@ function addNewRow(data_dictionary){ } function saveRowEdits(row){ - var cells = rowToEdit.querySelectorAll('td') + var cells = row.querySelectorAll('td') cells.forEach( cell => { // save dropdown values if (cell.querySelector('select')){ + var serviceSelector = cell.querySelector('select'); cell.textContent = serviceSelector.value; } else if (cell.querySelector('input')) { + var textbox = cell.querySelector('input'); var enteredValue = textbox.value; cell.textContent = enteredValue; cell.setAttribute('value', enteredValue); diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index 0c8b7a4..d62a023 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -6,8 +6,6 @@ import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import Table from '../../components/table/table.js' import Sidebar from "../../components/sidebar/sidebar.js"; import { DATA_ROOT, fringe, cola, merit } from "../../init.js" -import { createDropdownFromJSON } from "../../components/form/form.js"; - const personnelColumns = [ { title: 'Job Name', className: 'job-name' }, @@ -16,6 +14,7 @@ const personnelColumns = [ { title: 'Service', className: 'service' }, { title: 'Total Cost (Baseline)', className: 'total-baseline', isCost: true }, { title: 'Total Cost (Supplementary)', className: 'total-supp', isCost: true }, + { title: 'Current Average Salary', className: 'avg-salary', isCost: true } ]; export function preparePageView(){ @@ -23,6 +22,7 @@ export function preparePageView(){ hideWelcomeButtons(); showPrompt(); showNavButtons(); + updateDisplayandTotals(); Sidebar.show(); hidePromptButtons(); Table.adjustWidth('90%'); @@ -33,9 +33,9 @@ export function preparePageView(){ } function personnelRowOnEdit(){ - createEditableCell('baseline-ftes'); - createEditableCell('supp-ftes'); - createSelectCell('service'); + Table.Cell.createTextbox('baseline-ftes'); + Table.Cell.createTextbox('supp-ftes'); + Table.Cell.createDropdown('service', DATA_ROOT + 'services.json'); } export async function initializePersonnelTable(){ @@ -48,37 +48,11 @@ export async function initializePersonnelTable(){ Table.Columns.addAtEnd( '0', 'Total Cost (Supplementary)'); Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, ' '); // assign cost classes - Table.Columns.assignClasses(personnelColumns) + Table.Columns.assignClasses(personnelColumns); // activate edit buttons - Table.Buttons.Edit.init(personnelRowOnEdit); -} - - -function createEditableCell(cellClass, attribute = 'value'){ - // get cell - const cell = document.querySelector(`.active-editing td.${cellClass}`); - // Create an input element to edit the value - var textbox = document.createElement('input'); - textbox.type = 'text'; - textbox.value = cell.textContent; - // Clear the current content and append the textbox to the cell - cell.innerHTML = ''; - cell.appendChild(textbox); - //cell.appendChild(feedback); + Table.Buttons.Edit.init(personnelRowOnEdit, updateDisplayandTotals); } -async function createSelectCell(cellClass){ - // get cell - const cell = document.querySelector(`.active-editing td.${cellClass}`); - // add service dropdown - const serviceDropdown = await createDropdownFromJSON(DATA_ROOT + 'services.json'); - serviceDropdown.value = cell.textContent; - // Clear the current content and append the textbox to the cell - cell.innerHTML = ''; - cell.appendChild(serviceDropdown); -} - - function calculateTotalCost(ftes, avg_salary, fringe, cola, merit){ return ftes * avg_salary * (1 + fringe) * (1 + cola) * (1 + merit); } @@ -99,7 +73,7 @@ function updateDisplayandTotals(){ // calcuate #FTEs x average salary + COLA adjustments + merit adjustments + fringe let total_baseline_cost = calculateTotalCost(baseline_ftes, avg_salary, fringe, cola, merit); let total_supp_cost = calculateTotalCost(supp_ftes, avg_salary, fringe, cola, merit); - + // update counters Sidebar.incrementStat('baseline-personnel', total_baseline_cost); Sidebar.incrementStat('supp-personnel', total_supp_cost); From ac0e6617c8a0d83c770c3caf205c23aed47c946f Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Thu, 27 Jun 2024 16:26:15 -0400 Subject: [PATCH 10/30] completed refactoring of table and sidebar --- css/common.css | 2 + js/components/table/subcomponents/buttons.js | 21 ++--- js/components/table/subcomponents/headers.js | 4 +- js/components/table/subcomponents/rows.js | 4 +- js/components/table/table.css | 2 +- js/components/table/table.js | 6 +- js/pages/02_baseline_landing_page/main.js | 1 + js/pages/04_personnel/helpers.js | 1 - js/pages/05_nonpersonnel/helpers.js | 98 +++----------------- js/pages/06_new_initiatives/helpers.js | 18 ++-- 10 files changed, 43 insertions(+), 114 deletions(-) diff --git a/css/common.css b/css/common.css index 2bb9994..1b3d941 100644 --- a/css/common.css +++ b/css/common.css @@ -33,6 +33,8 @@ body { cursor: pointer; padding: 10px; margin-top: 5px; + margin-right : 2px; + margin-left: 2px; border-radius: 10px; background-color: gray; color: white; diff --git a/js/components/table/subcomponents/buttons.js b/js/components/table/subcomponents/buttons.js index 3fc500a..82ffd41 100644 --- a/js/components/table/subcomponents/buttons.js +++ b/js/components/table/subcomponents/buttons.js @@ -13,7 +13,7 @@ function showButton(className){ return function() { var buttons = document.getElementsByClassName(className); for (var i = 0; i < buttons.length; i++) { - buttons[i].style.display = ''; + buttons[i].style.display = 'inline'; } } } @@ -22,16 +22,6 @@ function updateButtonText(className, text){ document.querySelector(`.${className}`).textContent = text; } -// button for adding a row - -const AddRow = { - hide: hideButton('btn-add'), - show: showButton('btn-add'), - updateText: function(text){ - updateButtonText('btn-add', text); - } -}; - // EDIT button function handleRowEdit(makeRowEditable, updateCallback){ @@ -97,10 +87,19 @@ const Confirm = { show: showButton('btn-confirm') }; +const AddRow = { + hide: hideButton('btn-add'), + show: showButton('btn-add'), + updateText: function(text){ + updateButtonText('btn-add', text); + } +}; + export const Buttons = { Delete: Delete, Edit : Edit, Confirm : Confirm, + AddRow : AddRow, edit_confirm_btns : Edit.html + Confirm.html , all_btns : Delete.html + Edit.html + Confirm.html } diff --git a/js/components/table/subcomponents/headers.js b/js/components/table/subcomponents/headers.js index 8e12194..1304c8f 100644 --- a/js/components/table/subcomponents/headers.js +++ b/js/components/table/subcomponents/headers.js @@ -21,10 +21,10 @@ function addTableHeaders(header_array){ thead.appendChild(headerRow); } -const Headers = { +const Header = { add: function(header_array){ addTableHeaders(header_array) } }; -export default Headers; \ No newline at end of file +export default Header; \ No newline at end of file diff --git a/js/components/table/subcomponents/rows.js b/js/components/table/subcomponents/rows.js index d5a3569..0668df8 100644 --- a/js/components/table/subcomponents/rows.js +++ b/js/components/table/subcomponents/rows.js @@ -1,3 +1,5 @@ +import Header from "./headers.js"; + function addNewRow(data_dictionary){ // Get the table element by its ID const table = document.getElementById('main-table'); @@ -5,7 +7,7 @@ function addNewRow(data_dictionary){ // check if header has already been added let header_row = table.querySelector('thead tr'); if (!header_row) { - addTableHeaders(table_id, Object.keys(data_dictionary)); + Header.add(Object.keys(data_dictionary)); } // add row of data diff --git a/js/components/table/table.css b/js/components/table/table.css index df26837..fd19d99 100644 --- a/js/components/table/table.css +++ b/js/components/table/table.css @@ -47,7 +47,7 @@ input { background-color: var(--spiritgreen); } -.active-editing { +.active-editing, .selected { background-color: var(--palegreen); } diff --git a/js/components/table/table.js b/js/components/table/table.js index 6a61d0c..372ea49 100644 --- a/js/components/table/table.js +++ b/js/components/table/table.js @@ -1,7 +1,7 @@ import Buttons from './subcomponents/buttons.js' import Cell from './subcomponents/cells.js' import Columns from './subcomponents/columns.js' -import Headers from './subcomponents/headers.js' +import Header from './subcomponents/headers.js' import Rows from './subcomponents/rows.js' function adjustTableWidth(width_pct){ @@ -24,14 +24,14 @@ function showTable(){ function hideTable(){ const table = document.getElementById('main-table'); table.style.display = 'none'; - Table.Buttons.AddRow.hide(); + Buttons.AddRow.hide(); } const Table = { Buttons : Buttons, Cell : Cell, Columns : Columns, - Headers : Headers, + Header : Header, Rows : Rows, // functions adjustWidth : function(width_pct){ diff --git a/js/pages/02_baseline_landing_page/main.js b/js/pages/02_baseline_landing_page/main.js index 2b60dbe..6e7c62f 100644 --- a/js/pages/02_baseline_landing_page/main.js +++ b/js/pages/02_baseline_landing_page/main.js @@ -29,5 +29,6 @@ export function loadBaselineLandingPage(){ Select one of your funds to begin.`); loadJSONIntoTable(DATA_ROOT + 'funds.json', 'main-table') + Table.adjustWidth('100%'); Table.show(); } \ No newline at end of file diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index d62a023..59f08d3 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -22,7 +22,6 @@ export function preparePageView(){ hideWelcomeButtons(); showPrompt(); showNavButtons(); - updateDisplayandTotals(); Sidebar.show(); hidePromptButtons(); Table.adjustWidth('90%'); diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/pages/05_nonpersonnel/helpers.js index 5d5c4e6..cfc8676 100644 --- a/js/pages/05_nonpersonnel/helpers.js +++ b/js/pages/05_nonpersonnel/helpers.js @@ -8,6 +8,10 @@ import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; import { formatCurrency } from "../../utils/utils.js"; +const nonPersonnelColumns = [ + { title: 'Request Total', className: 'request', isCost: true }, +]; + export function preparePageView(){ // prepare page view hideWelcomeButtons(); @@ -27,105 +31,27 @@ export async function initializeNonpersonnelTable(){ await loadJSONIntoTable(DATA_ROOT + 'nonpersonnel_data.json', 'main-table'); //after table is loaded, fill it Table.show(); - Table.Columns.addAtEnd(Table.Buttons.all(), "Select Action"); + Table.Columns.addAtEnd(Table.Buttons.all_btns, "Select Action"); // assign cost classes - Table.Columns.assignClass('Request Total', 'request'); - Table.Columns.addCostClass('Request Total'); - handleRowEdit(['request']); -} - -export function handleRowEdit(editable_col_classes){ - // attach an event listener to each edit button in every row - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].addEventListener('click', async function(event) { - // Determine what was clicked on within the table - var rowToEdit = event.target.closest('tr'); - // mark row as being edited - rowToEdit.classList.add('active-editing'); - - // turn relevant entries into textboxes - for (let i = 0; i < editable_col_classes.length; i++){ - createEditableCell(editable_col_classes[i]) - } - - // hide edit buttons - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].style.display = 'none'; - } - - initializeConfirmButton(rowToEdit); - }); - }; -} - -function createEditableCell(cellClass, attribute = 'value'){ - // get cell - const cell = document.querySelector(`.active-editing td.${cellClass}`); - // Create an input element to edit the value - var textbox = document.createElement('input'); - textbox.type = 'text'; - if (cell.classList.contains('cost')){ - textbox.value = cell.getAttribute('value'); - } else { - textbox.value = cell.textContent; - } - // Clear the current content and append the textbox to the cell - cell.innerHTML = ''; - cell.appendChild(textbox); - //cell.appendChild(feedback); + Table.Columns.assignClasses(nonPersonnelColumns); + Table.Buttons.Edit.init(nonPersonnelRowOnEdit, updateDisplayandTotals); } -function initializeConfirmButton(rowToEdit){ - // show confirm button - const confirm_btn = rowToEdit.querySelector(".btn-confirm"); - confirm_btn.style.display = 'inline'; - // add event listener for confirm - confirm_btn.addEventListener('click', function(event){ - // get current row - const rowToEdit = event.target.closest('tr'); - var textboxes = rowToEdit.querySelectorAll('input'); - // save all text in textboxes - textboxes.forEach( textbox => { - var enteredValue = textbox.value; - var cell = textbox.closest('td'); - cell.setAttribute('value', enteredValue); - if (cell.classList.contains('cost')){ - cell.textContent = formatCurrency(enteredValue); - } else { - cell.textContent = enteredValue; - } - }) - - // update values in sidebar - updateDisplayandTotals(); - - // make row no longer green - rowToEdit.classList.remove('active-editing'); - - // show edit buttons - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].style.display = 'inline'; - } - - // hide confirm button - confirm_btn.style.display = 'none'; - }); +function nonPersonnelRowOnEdit(){ + Table.Cell.createTextbox('request'); } // update sidebar and also cost totals when the FTEs are edited function updateDisplayandTotals(){ // initialize - updateSidebarStat('baseline-nonpersonnel', 0); + Sidebar.updateStat('baseline-nonpersonnel', 0); // calculate for each row let rows = document.getElementsByTagName('tr'); for (let i = 1; i < rows.length; i++){ // fetch values for calculations - let request = getCellValue(rows[i], 'request'); + let request = Table.Cell.getValue(rows[i], 'request'); // update counters - incrementSidebarStat('baseline-nonpersonnel', request); + Sidebar.incrementStat('baseline-nonpersonnel', request); } } diff --git a/js/pages/06_new_initiatives/helpers.js b/js/pages/06_new_initiatives/helpers.js index d839a30..dc05f0b 100644 --- a/js/pages/06_new_initiatives/helpers.js +++ b/js/pages/06_new_initiatives/helpers.js @@ -17,9 +17,9 @@ export function initializePageView() { // Prepare page view hideWelcomeButtons(); showNavButtons(); - hideSidebar(); + Sidebar.hide(); showPrompt(); - hideTable('main-table'); + Table.hide(); } export function setUpModal() { @@ -37,7 +37,7 @@ export function setUpForm() { addTextarea('Explain why this initiative is necessary and describe its potential impact.', 'Explanation', true); addNumericInput('Estimate of ADDITONAL personnel cost?', 'Personnel Cost', true); addNumericInput('Estimate of ADDITONAL nonpersonnel cost?', 'Non-personnel Cost', true); - addNumericInput('Estimate of total ADDITIONAL cost?', 'Total Cost', true); + addNumericInput('Estimate of TOTAL ADDITIONAL cost?', 'Total Cost', true); addSubmitButtonToForm(); // Initialize form submission to table data handleFormSubmissions(); @@ -45,9 +45,9 @@ export function setUpForm() { export function setUpTable() { // Set up table - clearTable('main-table'); - adjustTableWidth('main-table', '70%'); - updateAddButtonText('Add another new initiative'); + Table.clear(); + Table.adjustWidth('70%'); + Table.Buttons.AddRow.updateText('Add another new initiative'); } export function handleNavigation() { @@ -68,9 +68,9 @@ export function handleFormSubmissions(event){ hidePrompt(); // add data to table - addNewRow('main-table', responses); - showTable('main-table'); - showAddButton(); + Table.Rows.add(responses); + Table.show(); + Table.Buttons.AddRow.show(); // TODO: save table data // TODO: edit cost to show currency correctly } From 35342db12ba55ccef9d4e993867470e794ad285c Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Thu, 27 Jun 2024 16:49:40 -0400 Subject: [PATCH 11/30] added a Body component with a clearAll function to initialize each page view --- css/common.css | 43 ---------- index.html | 1 + js/components/body/body.css | 3 + js/components/body/body.js | 27 +++++++ js/components/table/table.css | 8 ++ js/pages/00_welcome/helpers.js | 15 +--- js/pages/01_upload/main.js | 9 +-- js/pages/02_baseline_landing_page/main.js | 9 +-- js/pages/03_revenue/main.js | 10 +-- js/pages/04.5_OT/main.js | 12 +-- .../04_personnel/archive/main_archived.js | 47 ----------- .../04_personnel/archive/rollup-helpers.js | 78 ------------------- js/pages/04_personnel/helpers.js | 7 +- js/pages/05_nonpersonnel/helpers.js | 8 +- js/pages/06_new_initiatives/helpers.js | 7 +- js/pages/07_summary/main.js | 6 +- 16 files changed, 61 insertions(+), 229 deletions(-) create mode 100644 js/components/body/body.css create mode 100644 js/components/body/body.js delete mode 100644 js/pages/04_personnel/archive/main_archived.js delete mode 100644 js/pages/04_personnel/archive/rollup-helpers.js diff --git a/css/common.css b/css/common.css index 1b3d941..5c0da89 100644 --- a/css/common.css +++ b/css/common.css @@ -17,16 +17,6 @@ font-size: 14px; } -/* Every page */ - -/* start by hiding everything */ -#welcome-page{display: none;} -#prompt-div{display: none;} - -body { - margin: 10px; -} - /* Button styling */ .btn { @@ -40,36 +30,3 @@ body { color: white; } -/* Action buttons */ - -.action-btns { - text-align: center; -} - -.delete { - background-color: #ee9286; -} -.delete .btn-delete, .keep .btn-carryover, .supp .btn-supplemental { - background-color: #373A37; -} -.keep { - background-color: #90ee90; -} -.supp { - background-color: #e0b472; -} - -.btn-delete {background-color: var(--orange);} -.btn-supplemental { background-color: var(--yellow);} -.btn-carryover {background-color: var(--green);} - - -.error-message { - color: red; -} - -/* confirm button */ -.confirm-btn:hover { - background-color: var(--green); -} - diff --git a/index.html b/index.html index 077bf02..9357eab 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ + diff --git a/js/components/body/body.css b/js/components/body/body.css new file mode 100644 index 0000000..e0ba1a2 --- /dev/null +++ b/js/components/body/body.css @@ -0,0 +1,3 @@ +body { + margin: 10px; +} \ No newline at end of file diff --git a/js/components/body/body.js b/js/components/body/body.js new file mode 100644 index 0000000..d1732ad --- /dev/null +++ b/js/components/body/body.js @@ -0,0 +1,27 @@ +import { hideWelcomeButtons } from '../../components/welcome/welcome.js' +import { hideModal, removeModalLink } from '../modal/modal.js'; +import { hideNavButtons } from '../nav_buttons/nav_buttons.js'; +import { hidePrompt, hidePromptButtons } from '../prompt/prompt.js'; +import Sidebar from '../sidebar/sidebar.js'; +import Table from '../table/table.js'; + + +function clearAll() { + // hide everything in the body + hideWelcomeButtons(); + hideModal(); + hideNavButtons(); + hidePrompt(); + Table.hide(); + Sidebar.hide(); + hidePromptButtons(); + + // todo: fix this function to remove id in modal folder + removeModalLink('option1', 'main-modal'); +} + +export const Body = { + clearAll : clearAll +} + +export default Body; \ No newline at end of file diff --git a/js/components/table/table.css b/js/components/table/table.css index fd19d99..bad8e1d 100644 --- a/js/components/table/table.css +++ b/js/components/table/table.css @@ -36,6 +36,10 @@ input { display: none; } +.btn-delete { + background-color: var(--orange); +} + #add-btn-div { display: flex; justify-content: center; /* Aligns horizontally */ @@ -53,4 +57,8 @@ input { .btn-confirm { display: none; +} + +.confirm-btn:hover { + background-color: var(--green); } \ No newline at end of file diff --git a/js/pages/00_welcome/helpers.js b/js/pages/00_welcome/helpers.js index 84f47e5..58b6ce6 100644 --- a/js/pages/00_welcome/helpers.js +++ b/js/pages/00_welcome/helpers.js @@ -1,25 +1,16 @@ - -import { hidePrompt } from '../../components/prompt/prompt.js' -import { hideNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import Sidebar from '../../components/sidebar/sidebar.js' -import Table from '../../components/table/table.js' -import { updateSubtitle } from '../../components/header/header.js' + import { updateSubtitle } from '../../components/header/header.js' import { unhideWelcomeButtons } from '../../components/welcome/welcome.js' import { loadNewInitiatives } from '../06_new_initiatives/main.js' -import { loadRevenuePage } from '../03_revenue/main.js' -import { loadPersonnelPage } from '../04_personnel/main.js' import { loadSummaryPage } from '../07_summary/main.js' import { loadBaselineLandingPage } from '../02_baseline_landing_page/main.js' import { loadUploadPage } from '../01_upload/main.js' +import Body from '../../components/body/body.js' export function initializePageView(){ // page set up - Table.hide(); - Sidebar.hide(); + Body.clearAll(); updateSubtitle("Welcome"); unhideWelcomeButtons(); - hidePrompt(); - hideNavButtons(); } export function addLinks(){ diff --git a/js/pages/01_upload/main.js b/js/pages/01_upload/main.js index 18a6e8b..6074974 100644 --- a/js/pages/01_upload/main.js +++ b/js/pages/01_upload/main.js @@ -1,23 +1,18 @@ import { updatePageState } from "../../utils/storage-handlers.js"; -import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, addPromptButtonAction, updatePromptButtons, hidePromptButtons } from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import Table from '../../components/table/table.js' -import Sidebar from '../../components/sidebar/sidebar.js' import { initializeWelcomePage } from "../00_welcome/main.js"; +import Body from "../../components/body/body.js"; export function loadUploadPage(){ //update page state updatePageState('upload'); // prepare page view - hideWelcomeButtons(); + Body.clearAll(); showPrompt(); - hidePromptButtons(); showNavButtons(); - Table.hide(); - Sidebar.hide(); // update page text updateSubtitle('Excel Upload'); diff --git a/js/pages/02_baseline_landing_page/main.js b/js/pages/02_baseline_landing_page/main.js index 6e7c62f..9282e18 100644 --- a/js/pages/02_baseline_landing_page/main.js +++ b/js/pages/02_baseline_landing_page/main.js @@ -1,25 +1,20 @@ import { updatePageState } from "../../utils/storage-handlers.js"; -import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, hidePromptButtons } from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import Table from "../../components/table/table.js"; -import Sidebar from '../../components/sidebar/sidebar.js' -import { removeModalLink } from '../../components/modal/modal.js' import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; +import Body from "../../components/body/body.js"; export function loadBaselineLandingPage(){ //update page state updatePageState('baseline-landing'); // prepare page view - hideWelcomeButtons(); + Body.clearAll(); showPrompt(); showNavButtons(); - Sidebar.hide(); - removeModalLink('option1', 'main-modal'); - hidePromptButtons(); // update page text updateSubtitle('Baseline Budget Request'); diff --git a/js/pages/03_revenue/main.js b/js/pages/03_revenue/main.js index ae5ead9..67dcb86 100644 --- a/js/pages/03_revenue/main.js +++ b/js/pages/03_revenue/main.js @@ -1,14 +1,11 @@ import { updatePageState } from '../../utils/storage-handlers.js' -import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction } from '../../components/prompt/prompt.js' import { nextPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import Table from '../../components/table/table.js' -import Sidebar from '../../components/sidebar/sidebar.js' import { formatCurrency } from '../../utils/utils.js' -import { removeModalLink } from '../../components/modal/modal.js' import { REVENUE } from '../../init.js' +import Body from '../../components/body/body.js' export function loadRevenuePage() { @@ -16,12 +13,9 @@ export function loadRevenuePage() { updatePageState('revenue'); // prepare page view - hideWelcomeButtons(); + Body.clearAll(); showPrompt(); showNavButtons(); - Table.hide(); - Sidebar.hide(); - removeModalLink('option1', 'main-modal'); // update page text updateSubtitle('Revenue Projections'); diff --git a/js/pages/04.5_OT/main.js b/js/pages/04.5_OT/main.js index 170c2bb..75f57c3 100644 --- a/js/pages/04.5_OT/main.js +++ b/js/pages/04.5_OT/main.js @@ -1,23 +1,17 @@ -import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' -import { showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction, hidePrompt, hidePromptButtons } from '../../components/prompt/prompt.js' +import { showPrompt, updatePrompt } from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import Table from '../../components/table/table.js' -import Sidebar from '../../components/sidebar/sidebar.js' - import { updatePageState } from "../../utils/storage-handlers.js"; +import Body from '../../components/body/body.js'; export function loadOTPage(){ //update page state updatePageState('overtime'); // prepare page view - hideWelcomeButtons(); + Body.clearAll(); showPrompt(); showNavButtons(); - Sidebar.hide(); - Table.hide(); - hidePromptButtons(); // update page text updateSubtitle('Overtime Estimates'); diff --git a/js/pages/04_personnel/archive/main_archived.js b/js/pages/04_personnel/archive/main_archived.js deleted file mode 100644 index 54e9d6e..0000000 --- a/js/pages/04_personnel/archive/main_archived.js +++ /dev/null @@ -1,47 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - - // // Load from last local storage - // loadTableData("employeeTableData"); - - // // Add an event listener for the save button - // document.getElementById('save').addEventListener('click', function() { - // saveTableData("employeeTableData"); - // }); - - // // Add an event listener for the download button - // document.getElementById('XLSX-download').addEventListener('click', function() { - // saveTableData("employee-table"); - // downloadTableAsExcel('employeeTableData', 'Personnel', 'table-export'); - // }); - - // Mark row to be edited on edit button click - var editButtons = document.getElementsByClassName('btn-edit'); - for (var i = 0; i < editButtons.length; i++) { - editButtons[i].addEventListener('click', handleAccountEdit); - }; - // Remove edit marker when finished - document.getElementById('modal-close-x').addEventListener('click', exitAccountEditModal); - document.getElementById('modal-done-btn').addEventListener('click', exitAccountEditModal); - - // Update account string based on info in modal dropdowns - document.getElementById('dropdown-fund').addEventListener("change", function(event){ - updateAccountString('dropdown-fund', 'fund-string'); - }); - document.getElementById('dropdown-approp').addEventListener("change", function(event){ - updateAccountString('dropdown-approp', 'approp-string'); - }); - document.getElementById('dropdown-cc').addEventListener("change", function(event){ - updateAccountString('dropdown-cc', 'cc-string'); - }); - - // Make FTEs editable - applyEditableCells('.ftes', 'value', null, updateDisplayandTotals, validateNumber) - - // Initialize continue button - document.getElementById('continue-btn').addEventListener('click', continueToNonPersonnel); - -}); - - - - diff --git a/js/pages/04_personnel/archive/rollup-helpers.js b/js/pages/04_personnel/archive/rollup-helpers.js deleted file mode 100644 index 931e08f..0000000 --- a/js/pages/04_personnel/archive/rollup-helpers.js +++ /dev/null @@ -1,78 +0,0 @@ -// variables on the salary -fringe = 0.36 -cola = 0.02 -merit = 0.02 - - -function updateAccountString(dropdown_id, string_class){ - var dropdown = document.getElementById(dropdown_id); - var account_num = dropdown.options[dropdown.selectedIndex].value; - // find correct string to update - var rowToEdit = document.getElementById('editing'); - var account_span = rowToEdit.getElementsByClassName(string_class)[0]; - account_span.textContent = account_num; -} - -function handleAccountEdit(event) { - // Determine what was clicked on within the table - var rowToEdit = event.target.closest('tr'); - // mark row as being edited - rowToEdit.id = 'editing'; - var job_name = rowToEdit.cells[0].textContent; - document.getElementById('job-name').textContent = job_name; -} - -function exitAccountEditModal() { - // remove marker that row is being actively edited - document.getElementById('editing').removeAttribute('id'); -} - - -// check if all service boxes are filled -function validateServiceSelections(){ - let service_dropdowns = document.querySelectorAll(".service") - let validated = true; - service_dropdowns.forEach(function(dropdown) { - if (!dropdown.value || dropdown.value.trim() === "") { - // Found a dropdown with an empty value, return false - validated = false; - } - }); - // All dropdowns have a non-empty value - return validated; -} - -function showServiceErrors(){ - let service_dropdowns = document.querySelectorAll(".service") - service_dropdowns.forEach(function(dropdown) { - - const cell = dropdown.parentElement; - - // Clear any previous error message - const existingErrorMessage = cell.querySelector('.error-message'); - if (existingErrorMessage) { - cell.removeChild(existingErrorMessage); - } - - if (!dropdown.value || dropdown.value.trim() === "") { - // Create a new span element for the error message - const errorMessage = document.createElement('span'); - errorMessage.textContent = "This field is required"; - errorMessage.classList.add('error-message'); - - // Append the error message span to the cell - cell.appendChild(errorMessage); - } - }); -} - -// function to happen on click of continue button -function continueToNonPersonnel(){ - if (validateServiceSelections()){ - saveCounters(); - saveTableData(table_id = "rollup-table", save_as = "rollup_table"); - window.location.href = "05_nonpersonnel.html"; - } else { - showServiceErrors(); - } -} \ No newline at end of file diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index 59f08d3..d3000b4 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -1,11 +1,11 @@ -import { hideWelcomeButtons } from "../../components/welcome/welcome.js"; -import { hidePromptButtons, showPrompt, updatePrompt } from "../../components/prompt/prompt.js"; +import { showPrompt, updatePrompt } from "../../components/prompt/prompt.js"; import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import Table from '../../components/table/table.js' import Sidebar from "../../components/sidebar/sidebar.js"; import { DATA_ROOT, fringe, cola, merit } from "../../init.js" +import Body from "../../components/body/body.js"; const personnelColumns = [ { title: 'Job Name', className: 'job-name' }, @@ -19,11 +19,10 @@ const personnelColumns = [ export function preparePageView(){ // prepare page view - hideWelcomeButtons(); + Body.clearAll(); showPrompt(); showNavButtons(); Sidebar.show(); - hidePromptButtons(); Table.adjustWidth('90%'); // update page text diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/pages/05_nonpersonnel/helpers.js index cfc8676..1c25ef0 100644 --- a/js/pages/05_nonpersonnel/helpers.js +++ b/js/pages/05_nonpersonnel/helpers.js @@ -1,12 +1,11 @@ -import { hideWelcomeButtons } from "../../components/welcome/welcome.js"; -import { showPrompt, hidePromptButtons, updatePrompt } from "../../components/prompt/prompt.js"; +import { showPrompt, updatePrompt } from "../../components/prompt/prompt.js"; import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import Sidebar from "../../components/sidebar/sidebar.js"; import Table from "../../components/table/table.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; -import { formatCurrency } from "../../utils/utils.js"; +import Body from "../../components/body/body.js"; const nonPersonnelColumns = [ { title: 'Request Total', className: 'request', isCost: true }, @@ -14,11 +13,10 @@ const nonPersonnelColumns = [ export function preparePageView(){ // prepare page view - hideWelcomeButtons(); + Body.clearAll(); showPrompt(); showNavButtons(); Sidebar.show(); - hidePromptButtons(); Table.adjustWidth('100%'); // update page text updateSubtitle('Non-Personnel'); diff --git a/js/pages/06_new_initiatives/helpers.js b/js/pages/06_new_initiatives/helpers.js index dc05f0b..ffd7cbc 100644 --- a/js/pages/06_new_initiatives/helpers.js +++ b/js/pages/06_new_initiatives/helpers.js @@ -1,12 +1,11 @@ -import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' import { hidePrompt, showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction } from '../../components/prompt/prompt.js' import { showNavButtons, nextPage } from '../../components/nav_buttons/nav_buttons.js' import { addModalLink, updateModalTitle, clearModal, hideModal } from '../../components/modal/modal.js' import { fetchAllResponses, addTextarea, addTextInput, addNumericInput, addSubmitButtonToForm, addForm } from '../../components/form/form.js' -import Sidebar from '../../components/sidebar/sidebar.js' import Table from '../../components/table/table.js' +import Body from '../../components/body/body.js' export function initializePageView() { // Load text @@ -15,11 +14,9 @@ export function initializePageView() { updatePromptButtons('Yes', 'No'); // Prepare page view - hideWelcomeButtons(); + Body.clearAll(); showNavButtons(); - Sidebar.hide(); showPrompt(); - Table.hide(); } export function setUpModal() { diff --git a/js/pages/07_summary/main.js b/js/pages/07_summary/main.js index 69f0f39..26df965 100644 --- a/js/pages/07_summary/main.js +++ b/js/pages/07_summary/main.js @@ -6,18 +6,16 @@ import { hideNavButtons, lastPage, showNavButtons } from '../../components/nav_b import Sidebar from '../../components/sidebar/sidebar.js' import Table from "../../components/table/table.js"; import { initializeWelcomePage } from "../00_welcome/main.js"; +import Body from "../../components/body/body.js"; export function loadSummaryPage(){ //update page state updatePageState('summary'); // prepare page view - hideWelcomeButtons(); + Body.clearAll(); showPrompt(); - Table.hide(); - Sidebar.hide(); updatePromptButtons('Submit', 'Go back and edit'); - hideNavButtons(); // update page text updateSubtitle('Summary'); From 76b09b17e0c15c42e9f1da92e3e966da0ca17f92 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 1 Jul 2024 13:00:35 -0400 Subject: [PATCH 12/30] modularized prompt --- js/components/body/body.js | 5 +- js/components/nav_buttons/nav_buttons.js | 7 ++- js/components/prompt/prompt.js | 39 ++++++---------- js/components/prompt/subcomponents/buttons.js | 46 +++++++++++++++++++ js/components/prompt/subcomponents/text.js | 21 +++++++++ js/pages/01_upload/main.js | 9 ++-- js/pages/02_baseline_landing_page/main.js | 5 +- js/pages/03_revenue/main.js | 10 ++-- js/pages/04.5_OT/main.js | 5 +- js/pages/04_personnel/helpers.js | 5 +- js/pages/05_nonpersonnel/helpers.js | 5 +- js/pages/06_new_initiatives/helpers.js | 20 ++++---- js/pages/06_new_initiatives/main.js | 3 +- js/pages/07_summary/main.js | 14 ++---- 14 files changed, 118 insertions(+), 76 deletions(-) create mode 100644 js/components/prompt/subcomponents/buttons.js create mode 100644 js/components/prompt/subcomponents/text.js diff --git a/js/components/body/body.js b/js/components/body/body.js index d1732ad..ec42314 100644 --- a/js/components/body/body.js +++ b/js/components/body/body.js @@ -1,7 +1,7 @@ import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { hideModal, removeModalLink } from '../modal/modal.js'; import { hideNavButtons } from '../nav_buttons/nav_buttons.js'; -import { hidePrompt, hidePromptButtons } from '../prompt/prompt.js'; +import Prompt from '../prompt/prompt.js'; import Sidebar from '../sidebar/sidebar.js'; import Table from '../table/table.js'; @@ -11,10 +11,9 @@ function clearAll() { hideWelcomeButtons(); hideModal(); hideNavButtons(); - hidePrompt(); + Prompt.hide(); Table.hide(); Sidebar.hide(); - hidePromptButtons(); // todo: fix this function to remove id in modal folder removeModalLink('option1', 'main-modal'); diff --git a/js/components/nav_buttons/nav_buttons.js b/js/components/nav_buttons/nav_buttons.js index 95082ab..7dcb407 100644 --- a/js/components/nav_buttons/nav_buttons.js +++ b/js/components/nav_buttons/nav_buttons.js @@ -2,16 +2,15 @@ import { loadPageState } from '../../utils/storage-handlers.js' import { PAGES } from '../../init.js' -export function hideNavButtons() { +function hideNavButtons() { document.getElementById('nav-btns').style.display = 'none'; } -export function showNavButtons() { +function showNavButtons() { document.getElementById('nav-btns').style.display = 'block'; } -// imputs next and last should be functions to render the appropriate pages -export function initializeNavButtons(){ +function initializeNavButtons(){ // initialize last button const last_btn = document.getElementById('btn-last'); last_btn.addEventListener('click', lastPage); diff --git a/js/components/prompt/prompt.js b/js/components/prompt/prompt.js index 0c38dbc..0f25585 100644 --- a/js/components/prompt/prompt.js +++ b/js/components/prompt/prompt.js @@ -1,29 +1,18 @@ -export function showPrompt(){ - document.getElementById("prompt-div").style.display = "block"; -} - -export function hidePrompt(){ - document.getElementById('prompt-div').style.display = 'none'; -} - -export function updatePrompt(prompt){ - document.getElementById('prompt').textContent = prompt; -} - -export function updatePromptButtons(option1, option2){ - document.getElementById('option1').textContent = option1; - document.getElementById('option2').textContent = option2; - // make buttons visible - document.getElementById('option1').style.display = 'inline'; - document.getElementById('option2').style.display = 'inline'; -} +import Text from "./subcomponents/text.js"; +import Buttons from "./subcomponents/buttons.js"; -export function addPromptButtonAction(button_id, action_fn){ - document.getElementById(button_id).addEventListener('click', action_fn); +export const Prompt = { + Text : Text, + Buttons : Buttons, + hide : function(){ + Text.hide(); + Buttons.hide(); + }, + show : function(){ + Text.show(); + Buttons.show(); + } } -export function hidePromptButtons(){ - document.getElementById('option1').style.display = 'none'; - document.getElementById('option2').style.display = 'none'; -} \ No newline at end of file +export default Prompt \ No newline at end of file diff --git a/js/components/prompt/subcomponents/buttons.js b/js/components/prompt/subcomponents/buttons.js new file mode 100644 index 0000000..076c0f8 --- /dev/null +++ b/js/components/prompt/subcomponents/buttons.js @@ -0,0 +1,46 @@ +function showPromptButton(id){ + // make buttons visible + document.getElementById(id).style.display = 'inline'; +} + +function updatePromptButton(id, text){ + document.getElementById(id).textContent = text; + showPromptButton(id); +} + +function addPromptButtonAction(button_id, action_fn){ + document.getElementById(button_id).addEventListener('click', action_fn); +} + +function hidePromptButton(id){ + document.getElementById(id).style.display = 'none'; +} + +export const Left = { + show : showPromptButton('option1'), + hide : hidePromptButton('option1'), + updateText : function(text) { updatePromptButton('option1', text) }, + addAction : function(action_fn) { addPromptButtonAction('option1', action_fn) } +} + +export const Right = { + show : showPromptButton('option2'), + hide : hidePromptButton('option2'), + updateText : function(text) { updatePromptButton('option2', text) }, + addAction : function(action_fn) { addPromptButtonAction('option2', action_fn) } +} + +export const Buttons = { + Left : Left, + Right : Right, + show : function() { + showPromptButton('option1'); + showPromptButton('option2'); + }, + hide : function() { + hidePromptButton('option1'); + hidePromptButton('option2'); + } +} + +export default Buttons; \ No newline at end of file diff --git a/js/components/prompt/subcomponents/text.js b/js/components/prompt/subcomponents/text.js new file mode 100644 index 0000000..b693ad8 --- /dev/null +++ b/js/components/prompt/subcomponents/text.js @@ -0,0 +1,21 @@ +function showPrompt(){ + document.getElementById("prompt-div").style.display = "block"; +} + +function hidePrompt(){ + document.getElementById('prompt-div').style.display = 'none'; +} + + +function updatePrompt(prompt){ + document.getElementById('prompt').textContent = prompt; + showPrompt(); +} + +export const Text = { + show : showPrompt, + hide : hidePrompt, + update : function(text) { updatePrompt(text) } +} + +export default Text; \ No newline at end of file diff --git a/js/pages/01_upload/main.js b/js/pages/01_upload/main.js index 6074974..b48cc50 100644 --- a/js/pages/01_upload/main.js +++ b/js/pages/01_upload/main.js @@ -1,6 +1,6 @@ import { updatePageState } from "../../utils/storage-handlers.js"; import { updateSubtitle } from '../../components/header/header.js' -import { showPrompt, updatePrompt, addPromptButtonAction, updatePromptButtons, hidePromptButtons } from '../../components/prompt/prompt.js' +import Prompt from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { initializeWelcomePage } from "../00_welcome/main.js"; import Body from "../../components/body/body.js"; @@ -11,12 +11,13 @@ export function loadUploadPage(){ // prepare page view Body.clearAll(); - showPrompt(); showNavButtons(); // update page text updateSubtitle('Excel Upload'); + // TODO: update to make dynamic - updatePrompt(`Placeholder for Excel Upload`); - addPromptButtonAction('option2', initializeWelcomePage); + Prompt.Text.update(`Placeholder for Excel Upload`); + Prompt.Buttons.Right.addAction(initializeWelcomePage) + } \ No newline at end of file diff --git a/js/pages/02_baseline_landing_page/main.js b/js/pages/02_baseline_landing_page/main.js index 9282e18..e8f2a6f 100644 --- a/js/pages/02_baseline_landing_page/main.js +++ b/js/pages/02_baseline_landing_page/main.js @@ -1,6 +1,6 @@ import { updatePageState } from "../../utils/storage-handlers.js"; import { updateSubtitle } from '../../components/header/header.js' -import { showPrompt, updatePrompt, hidePromptButtons } from '../../components/prompt/prompt.js' +import Prompt from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import Table from "../../components/table/table.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; @@ -13,13 +13,12 @@ export function loadBaselineLandingPage(){ // prepare page view Body.clearAll(); - showPrompt(); showNavButtons(); // update page text updateSubtitle('Baseline Budget Request'); // TODO: update to make dynamic - updatePrompt(`We will now ask you a series of questions about your BASELINE budget request. + Prompt.Text.update(`We will now ask you a series of questions about your BASELINE budget request. At the end, we will ask you about any new initiatives (ie. supplemental requests). Select one of your funds to begin.`); diff --git a/js/pages/03_revenue/main.js b/js/pages/03_revenue/main.js index 67dcb86..d58ccf1 100644 --- a/js/pages/03_revenue/main.js +++ b/js/pages/03_revenue/main.js @@ -1,6 +1,6 @@ import { updatePageState } from '../../utils/storage-handlers.js' import { updateSubtitle } from '../../components/header/header.js' -import { showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction } from '../../components/prompt/prompt.js' +import Prompt from '../../components/prompt/prompt.js' import { nextPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { formatCurrency } from '../../utils/utils.js' @@ -14,17 +14,17 @@ export function loadRevenuePage() { // prepare page view Body.clearAll(); - showPrompt(); showNavButtons(); // update page text updateSubtitle('Revenue Projections'); // TODO: update to make dynamic - updatePrompt(`Your revenue projection for FY26 is ${formatCurrency(REVENUE, true)}`); - updatePromptButtons('Confirm and continue.', "This doesn't look right"); + Prompt.Text.update(`Your revenue projection for FY26 is ${formatCurrency(REVENUE, true)}`); + Prompt.Buttons.Left.updateText('Confirm and continue.'); + Prompt.Buttons.Right.updateText("This doesn't look right"); // clicking 'confirm and continue' will also take us to the next page - addPromptButtonAction('option1', nextPage); + Prompt.Buttons.Left.addAction(nextPage); // TODO: allow user to edit revenue here // addPromptButtonAction('option2', nextPage); diff --git a/js/pages/04.5_OT/main.js b/js/pages/04.5_OT/main.js index 75f57c3..75f4437 100644 --- a/js/pages/04.5_OT/main.js +++ b/js/pages/04.5_OT/main.js @@ -1,5 +1,5 @@ import { updateSubtitle } from '../../components/header/header.js' -import { showPrompt, updatePrompt } from '../../components/prompt/prompt.js' +import Prompt from '../../components/prompt/prompt.js' import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { updatePageState } from "../../utils/storage-handlers.js"; import Body from '../../components/body/body.js'; @@ -10,11 +10,10 @@ export function loadOTPage(){ // prepare page view Body.clearAll(); - showPrompt(); showNavButtons(); // update page text updateSubtitle('Overtime Estimates'); // TODO: update to make dynamic - updatePrompt(`This is a placeholder for the OT estimates.`); + Prompt.Text.update(`This is a placeholder for the OT estimates.`); } \ No newline at end of file diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index d3000b4..1db7c36 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -1,4 +1,4 @@ -import { showPrompt, updatePrompt } from "../../components/prompt/prompt.js"; +import Prompt from "../../components/prompt/prompt.js"; import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; @@ -20,14 +20,13 @@ const personnelColumns = [ export function preparePageView(){ // prepare page view Body.clearAll(); - showPrompt(); showNavButtons(); Sidebar.show(); Table.adjustWidth('90%'); // update page text updateSubtitle('Personnel'); - updatePrompt('For each job in your department, select the service and request the number of baseline and supplemental FTEs.'); + Prompt.Text.update('For each job in your department, select the service and request the number of baseline and supplemental FTEs.'); } function personnelRowOnEdit(){ diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/pages/05_nonpersonnel/helpers.js index 1c25ef0..09a0a74 100644 --- a/js/pages/05_nonpersonnel/helpers.js +++ b/js/pages/05_nonpersonnel/helpers.js @@ -1,4 +1,4 @@ -import { showPrompt, updatePrompt } from "../../components/prompt/prompt.js"; +import Prompt from "../../components/prompt/prompt.js"; import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import Sidebar from "../../components/sidebar/sidebar.js"; import Table from "../../components/table/table.js"; @@ -14,13 +14,12 @@ const nonPersonnelColumns = [ export function preparePageView(){ // prepare page view Body.clearAll(); - showPrompt(); showNavButtons(); Sidebar.show(); Table.adjustWidth('100%'); // update page text updateSubtitle('Non-Personnel'); - updatePrompt('Select an action item for each non-personnel line item from last year.'); + Prompt.Text.update('Select an action item for each non-personnel line item from last year.'); updateDisplayandTotals(); } diff --git a/js/pages/06_new_initiatives/helpers.js b/js/pages/06_new_initiatives/helpers.js index ffd7cbc..5d24038 100644 --- a/js/pages/06_new_initiatives/helpers.js +++ b/js/pages/06_new_initiatives/helpers.js @@ -1,6 +1,6 @@ import { updateSubtitle } from '../../components/header/header.js' -import { hidePrompt, showPrompt, updatePrompt, updatePromptButtons, addPromptButtonAction } from '../../components/prompt/prompt.js' +import Prompt from '../../components/prompt/prompt.js' import { showNavButtons, nextPage } from '../../components/nav_buttons/nav_buttons.js' import { addModalLink, updateModalTitle, clearModal, hideModal } from '../../components/modal/modal.js' import { fetchAllResponses, addTextarea, addTextInput, addNumericInput, addSubmitButtonToForm, addForm } from '../../components/form/form.js' @@ -8,15 +8,16 @@ import Table from '../../components/table/table.js' import Body from '../../components/body/body.js' export function initializePageView() { - // Load text - updateSubtitle('New Initiatives'); - updatePrompt('Do you have any new initiatives for FY26?'); - updatePromptButtons('Yes', 'No'); - // Prepare page view Body.clearAll(); showNavButtons(); - showPrompt(); + Prompt.Buttons.Right.addAction(nextPage); + + // Load text + updateSubtitle('New Initiatives'); + Prompt.Text.update('Do you have any new initiatives for FY26?'); + Prompt.Buttons.Left.updateText('Yes'); + Prompt.Buttons.Right.updateText('No'); } export function setUpModal() { @@ -47,11 +48,6 @@ export function setUpTable() { Table.Buttons.AddRow.updateText('Add another new initiative'); } -export function handleNavigation() { - // clicking 'No' (no new initiatives) will also take us to the next page - addPromptButtonAction('option2', nextPage); -} - export function handleFormSubmissions(event){ // initialize form submission const modal = document.getElementById('main-modal'); diff --git a/js/pages/06_new_initiatives/main.js b/js/pages/06_new_initiatives/main.js index 63d636b..1ade087 100644 --- a/js/pages/06_new_initiatives/main.js +++ b/js/pages/06_new_initiatives/main.js @@ -1,5 +1,5 @@ -import { initializePageView, setUpModal, setUpForm, setUpTable, handleNavigation } from './helpers.js' +import { initializePageView, setUpModal, setUpForm, setUpTable } from './helpers.js' import { updatePageState } from '../../utils/storage-handlers.js' @@ -10,5 +10,4 @@ export function loadNewInitiatives() { setUpModal(); setUpForm(); setUpTable(); - handleNavigation(); } diff --git a/js/pages/07_summary/main.js b/js/pages/07_summary/main.js index 26df965..e29a8bd 100644 --- a/js/pages/07_summary/main.js +++ b/js/pages/07_summary/main.js @@ -1,10 +1,6 @@ import { updatePageState } from "../../utils/storage-handlers.js"; -import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { updateSubtitle } from '../../components/header/header.js' -import { showPrompt, updatePrompt, addPromptButtonAction, updatePromptButtons } from '../../components/prompt/prompt.js' -import { hideNavButtons, lastPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' -import Sidebar from '../../components/sidebar/sidebar.js' -import Table from "../../components/table/table.js"; +import Prompt from '../../components/prompt/prompt.js' import { initializeWelcomePage } from "../00_welcome/main.js"; import Body from "../../components/body/body.js"; @@ -14,12 +10,12 @@ export function loadSummaryPage(){ // prepare page view Body.clearAll(); - showPrompt(); - updatePromptButtons('Submit', 'Go back and edit'); + Prompt.Buttons.Left.updateText('Submit'); + Prompt.Buttons.Right.updateText('Go back and edit'); // update page text updateSubtitle('Summary'); // TODO: update to make dynamic - updatePrompt(`Placeholder for summary and any issues.`); - addPromptButtonAction('option2', initializeWelcomePage); + Prompt.Text.update(`Placeholder for summary and any issues.`); + Prompt.Buttons.Right.addAction(initializeWelcomePage); } \ No newline at end of file From a715bec9b02c35f5125d26713ffee88a8e1a925a Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 1 Jul 2024 13:18:20 -0400 Subject: [PATCH 13/30] updated nav button functions --- js/components/body/body.js | 4 +- js/components/nav_buttons/nav_buttons.js | 52 +++++---------------- js/init.js | 26 +---------- js/pages/01_upload/main.js | 4 +- js/pages/02_baseline_landing_page/main.js | 4 +- js/pages/03_revenue/main.js | 6 +-- js/pages/04.5_OT/main.js | 4 +- js/pages/04_personnel/helpers.js | 4 +- js/pages/05_nonpersonnel/helpers.js | 4 +- js/pages/06_new_initiatives/helpers.js | 5 +- js/utils/navigation-logic.js | 57 +++++++++++++++++++++++ 11 files changed, 88 insertions(+), 82 deletions(-) create mode 100644 js/utils/navigation-logic.js diff --git a/js/components/body/body.js b/js/components/body/body.js index ec42314..b6d44dc 100644 --- a/js/components/body/body.js +++ b/js/components/body/body.js @@ -1,6 +1,6 @@ import { hideWelcomeButtons } from '../../components/welcome/welcome.js' import { hideModal, removeModalLink } from '../modal/modal.js'; -import { hideNavButtons } from '../nav_buttons/nav_buttons.js'; +import NavButtons from '../nav_buttons/nav_buttons.js'; import Prompt from '../prompt/prompt.js'; import Sidebar from '../sidebar/sidebar.js'; import Table from '../table/table.js'; @@ -10,7 +10,7 @@ function clearAll() { // hide everything in the body hideWelcomeButtons(); hideModal(); - hideNavButtons(); + NavButtons.hide(); Prompt.hide(); Table.hide(); Sidebar.hide(); diff --git a/js/components/nav_buttons/nav_buttons.js b/js/components/nav_buttons/nav_buttons.js index 7dcb407..ff85a8f 100644 --- a/js/components/nav_buttons/nav_buttons.js +++ b/js/components/nav_buttons/nav_buttons.js @@ -1,14 +1,4 @@ - -import { loadPageState } from '../../utils/storage-handlers.js' -import { PAGES } from '../../init.js' - -function hideNavButtons() { - document.getElementById('nav-btns').style.display = 'none'; -} - -function showNavButtons() { - document.getElementById('nav-btns').style.display = 'block'; -} +import { nextPage, lastPage } from '../../utils/navigation-logic.js' function initializeNavButtons(){ // initialize last button @@ -19,36 +9,18 @@ function initializeNavButtons(){ next_btn.addEventListener('click', nextPage); } -export function nextPage(){ +function hideNavButtons() { + document.getElementById('nav-btns').style.display = 'none'; +} - var page_state = loadPageState(); - const keys = Object.keys(PAGES); - - // Find the index of the current key - const currentIndex = keys.indexOf(page_state); - - // Check if there is a next key - if (currentIndex >= 0 && currentIndex < keys.length - 1) { - // Get the next key - const nextKey = keys[currentIndex + 1]; - const nextFn = PAGES[nextKey]; - nextFn(); - } +function showNavButtons() { + document.getElementById('nav-btns').style.display = 'block'; + initializeNavButtons(); } -export function lastPage(){ +export const NavButtons = { + hide : hideNavButtons, + show : showNavButtons, +} - var page_state = loadPageState(); - const keys = Object.keys(PAGES); - - // Find the index of the current key - const currentIndex = keys.indexOf(page_state); - - // Check if there is a next key - if (currentIndex >= 1) { - // Get the next key - const lastKey = keys[currentIndex - 1]; - const lastFn = PAGES[lastKey]; - lastFn(); - } -} \ No newline at end of file +export default NavButtons; \ No newline at end of file diff --git a/js/init.js b/js/init.js index 941fd77..0ba40cb 100644 --- a/js/init.js +++ b/js/init.js @@ -1,15 +1,6 @@ // import functions -import { initializeWelcomePage } from './pages/00_welcome/main.js'; -import { loadNewInitiatives } from './pages/06_new_initiatives/main.js' -import { loadRevenuePage } from './pages/03_revenue/main.js' import { loadPageState } from './utils/storage-handlers.js' -import { initializeNavButtons } from './components/nav_buttons/nav_buttons.js'; -import { loadPersonnelPage } from './pages/04_personnel/main.js'; -import { loadOTPage } from './pages/04.5_OT/main.js'; -import { loadNonpersonnelPage } from './pages/05_nonpersonnel/main.js'; -import { loadBaselineLandingPage } from './pages/02_baseline_landing_page/main.js'; -import { loadSummaryPage } from './pages/07_summary/main.js'; -import { loadUploadPage } from './pages/01_upload/main.js'; +import { PAGES } from './utils/navigation-logic.js' // path for my laptop export let DATA_ROOT = '../../../data/law_dept_sample/' @@ -24,28 +15,13 @@ export var fringe = 0.36 export var cola = 0.02 export var merit = 0.02 -export let PAGES = { - 'welcome' : initializeWelcomePage, - 'upload' : loadUploadPage, - 'baseline-landing' : loadBaselineLandingPage, - 'revenue' : loadRevenuePage, - 'personnel' : loadPersonnelPage, - 'overtime' : loadOTPage, - 'nonpersonnel' : loadNonpersonnelPage, - 'new-inits' : loadNewInitiatives, - 'summary' : loadSummaryPage -} - document.addEventListener('DOMContentLoaded', function () { var page_state = loadPageState(); - initializeNavButtons(); // Use the page_state to access and call the corresponding function from PAGES if (PAGES[page_state]) { PAGES[page_state](); // Invokes the function if it exists in the PAGES map } else { console.error(`No page initializer found for state: ${page_state}`); - // Optionally, you can call a default function if page_state does not match - // initializeDefaultPage(); // Assume you have a default page initializer } }); \ No newline at end of file diff --git a/js/pages/01_upload/main.js b/js/pages/01_upload/main.js index b48cc50..bfcbc02 100644 --- a/js/pages/01_upload/main.js +++ b/js/pages/01_upload/main.js @@ -1,7 +1,7 @@ import { updatePageState } from "../../utils/storage-handlers.js"; import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' -import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' +import NavButtons from '../../components/nav_buttons/nav_buttons.js' import { initializeWelcomePage } from "../00_welcome/main.js"; import Body from "../../components/body/body.js"; @@ -11,7 +11,7 @@ export function loadUploadPage(){ // prepare page view Body.clearAll(); - showNavButtons(); + NavButtons.show(); // update page text updateSubtitle('Excel Upload'); diff --git a/js/pages/02_baseline_landing_page/main.js b/js/pages/02_baseline_landing_page/main.js index e8f2a6f..ae5e121 100644 --- a/js/pages/02_baseline_landing_page/main.js +++ b/js/pages/02_baseline_landing_page/main.js @@ -1,7 +1,7 @@ import { updatePageState } from "../../utils/storage-handlers.js"; import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' -import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' +import NavButtons from '../../components/nav_buttons/nav_buttons.js' import Table from "../../components/table/table.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; @@ -13,7 +13,7 @@ export function loadBaselineLandingPage(){ // prepare page view Body.clearAll(); - showNavButtons(); + NavButtons.show(); // update page text updateSubtitle('Baseline Budget Request'); diff --git a/js/pages/03_revenue/main.js b/js/pages/03_revenue/main.js index d58ccf1..a0ccd7b 100644 --- a/js/pages/03_revenue/main.js +++ b/js/pages/03_revenue/main.js @@ -1,11 +1,11 @@ import { updatePageState } from '../../utils/storage-handlers.js' import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' -import { nextPage, showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { formatCurrency } from '../../utils/utils.js' - import { REVENUE } from '../../init.js' import Body from '../../components/body/body.js' +import NavButtons from '../../components/nav_buttons/nav_buttons.js' +import { nextPage } from '../../utils/navigation-logic.js' export function loadRevenuePage() { @@ -14,7 +14,7 @@ export function loadRevenuePage() { // prepare page view Body.clearAll(); - showNavButtons(); + NavButtons.show(); // update page text updateSubtitle('Revenue Projections'); diff --git a/js/pages/04.5_OT/main.js b/js/pages/04.5_OT/main.js index 75f4437..3fcf2dc 100644 --- a/js/pages/04.5_OT/main.js +++ b/js/pages/04.5_OT/main.js @@ -1,8 +1,8 @@ import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' -import { showNavButtons } from '../../components/nav_buttons/nav_buttons.js' import { updatePageState } from "../../utils/storage-handlers.js"; import Body from '../../components/body/body.js'; +import NavButtons from '../../components/nav_buttons/nav_buttons.js'; export function loadOTPage(){ //update page state @@ -10,7 +10,7 @@ export function loadOTPage(){ // prepare page view Body.clearAll(); - showNavButtons(); + NavButtons.show(); // update page text updateSubtitle('Overtime Estimates'); diff --git a/js/pages/04_personnel/helpers.js b/js/pages/04_personnel/helpers.js index 1db7c36..daa5549 100644 --- a/js/pages/04_personnel/helpers.js +++ b/js/pages/04_personnel/helpers.js @@ -1,11 +1,11 @@ import Prompt from "../../components/prompt/prompt.js"; -import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import Table from '../../components/table/table.js' import Sidebar from "../../components/sidebar/sidebar.js"; import { DATA_ROOT, fringe, cola, merit } from "../../init.js" import Body from "../../components/body/body.js"; +import NavButtons from "../../components/nav_buttons/nav_buttons.js"; const personnelColumns = [ { title: 'Job Name', className: 'job-name' }, @@ -20,7 +20,7 @@ const personnelColumns = [ export function preparePageView(){ // prepare page view Body.clearAll(); - showNavButtons(); + NavButtons.show(); Sidebar.show(); Table.adjustWidth('90%'); diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/pages/05_nonpersonnel/helpers.js index 09a0a74..b6d6234 100644 --- a/js/pages/05_nonpersonnel/helpers.js +++ b/js/pages/05_nonpersonnel/helpers.js @@ -1,11 +1,11 @@ import Prompt from "../../components/prompt/prompt.js"; -import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js"; import Sidebar from "../../components/sidebar/sidebar.js"; import Table from "../../components/table/table.js"; import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; import Body from "../../components/body/body.js"; +import NavButtons from "../../components/nav_buttons/nav_buttons.js"; const nonPersonnelColumns = [ { title: 'Request Total', className: 'request', isCost: true }, @@ -14,7 +14,7 @@ const nonPersonnelColumns = [ export function preparePageView(){ // prepare page view Body.clearAll(); - showNavButtons(); + NavButtons.show(); Sidebar.show(); Table.adjustWidth('100%'); // update page text diff --git a/js/pages/06_new_initiatives/helpers.js b/js/pages/06_new_initiatives/helpers.js index 5d24038..75c6917 100644 --- a/js/pages/06_new_initiatives/helpers.js +++ b/js/pages/06_new_initiatives/helpers.js @@ -1,16 +1,17 @@ import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' -import { showNavButtons, nextPage } from '../../components/nav_buttons/nav_buttons.js' import { addModalLink, updateModalTitle, clearModal, hideModal } from '../../components/modal/modal.js' import { fetchAllResponses, addTextarea, addTextInput, addNumericInput, addSubmitButtonToForm, addForm } 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 { nextPage } from '../../utils/navigation-logic.js' export function initializePageView() { // Prepare page view Body.clearAll(); - showNavButtons(); + NavButtons.show(); Prompt.Buttons.Right.addAction(nextPage); // Load text diff --git a/js/utils/navigation-logic.js b/js/utils/navigation-logic.js new file mode 100644 index 0000000..52a04e1 --- /dev/null +++ b/js/utils/navigation-logic.js @@ -0,0 +1,57 @@ +import { initializeWelcomePage } from '../pages/00_welcome/main.js'; +import { loadNewInitiatives } from '../pages/06_new_initiatives/main.js' +import { loadRevenuePage } from '../pages/03_revenue/main.js' +import { loadPersonnelPage } from '../pages/04_personnel/main.js'; +import { loadOTPage } from '../pages/04.5_OT/main.js'; +import { loadNonpersonnelPage } from '../pages/05_nonpersonnel/main.js'; +import { loadBaselineLandingPage } from '../pages/02_baseline_landing_page/main.js'; +import { loadSummaryPage } from '../pages/07_summary/main.js'; +import { loadUploadPage } from '../pages/01_upload/main.js'; + +import { loadPageState } from './storage-handlers.js'; + +export let PAGES = { + 'welcome' : initializeWelcomePage, + 'upload' : loadUploadPage, + 'baseline-landing' : loadBaselineLandingPage, + 'revenue' : loadRevenuePage, + 'personnel' : loadPersonnelPage, + 'overtime' : loadOTPage, + 'nonpersonnel' : loadNonpersonnelPage, + 'new-inits' : loadNewInitiatives, + 'summary' : loadSummaryPage +} + +export function nextPage(){ + + var page_state = loadPageState(); + const keys = Object.keys(PAGES); + + // Find the index of the current key + const currentIndex = keys.indexOf(page_state); + + // Check if there is a next key + if (currentIndex >= 0 && currentIndex < keys.length - 1) { + // Get the next key + const nextKey = keys[currentIndex + 1]; + const nextFn = PAGES[nextKey]; + nextFn(); + } +} + +export function lastPage(){ + + var page_state = loadPageState(); + const keys = Object.keys(PAGES); + + // Find the index of the current key + const currentIndex = keys.indexOf(page_state); + + // Check if there is a next key + if (currentIndex >= 1) { + // Get the next key + const lastKey = keys[currentIndex - 1]; + const lastFn = PAGES[lastKey]; + lastFn(); + } +} \ No newline at end of file From 5bfebece6aabef0369dbf8e8e3940240f1e98a66 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 1 Jul 2024 13:21:17 -0400 Subject: [PATCH 14/30] renamed pages folder to views --- js/{pages => views}/00_welcome/helpers.js | 0 js/{pages => views}/00_welcome/main.js | 0 js/{pages => views}/01_upload/helpers.js | 0 js/{pages => views}/01_upload/main.js | 0 js/{pages => views}/02_baseline_landing_page/main.js | 0 js/{pages => views}/03_revenue/main.js | 0 js/{pages => views}/04.5_OT/main.js | 0 js/{pages => views}/04_personnel/helpers.js | 0 js/{pages => views}/04_personnel/main.js | 0 js/{pages => views}/05_nonpersonnel/helpers.js | 0 js/{pages => views}/05_nonpersonnel/main.js | 0 js/{pages => views}/06_new_initiatives/helpers.js | 0 js/{pages => views}/06_new_initiatives/main.js | 0 js/{pages => views}/07_summary/main.js | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename js/{pages => views}/00_welcome/helpers.js (100%) rename js/{pages => views}/00_welcome/main.js (100%) rename js/{pages => views}/01_upload/helpers.js (100%) rename js/{pages => views}/01_upload/main.js (100%) rename js/{pages => views}/02_baseline_landing_page/main.js (100%) rename js/{pages => views}/03_revenue/main.js (100%) rename js/{pages => views}/04.5_OT/main.js (100%) rename js/{pages => views}/04_personnel/helpers.js (100%) rename js/{pages => views}/04_personnel/main.js (100%) rename js/{pages => views}/05_nonpersonnel/helpers.js (100%) rename js/{pages => views}/05_nonpersonnel/main.js (100%) rename js/{pages => views}/06_new_initiatives/helpers.js (100%) rename js/{pages => views}/06_new_initiatives/main.js (100%) rename js/{pages => views}/07_summary/main.js (100%) diff --git a/js/pages/00_welcome/helpers.js b/js/views/00_welcome/helpers.js similarity index 100% rename from js/pages/00_welcome/helpers.js rename to js/views/00_welcome/helpers.js diff --git a/js/pages/00_welcome/main.js b/js/views/00_welcome/main.js similarity index 100% rename from js/pages/00_welcome/main.js rename to js/views/00_welcome/main.js diff --git a/js/pages/01_upload/helpers.js b/js/views/01_upload/helpers.js similarity index 100% rename from js/pages/01_upload/helpers.js rename to js/views/01_upload/helpers.js diff --git a/js/pages/01_upload/main.js b/js/views/01_upload/main.js similarity index 100% rename from js/pages/01_upload/main.js rename to js/views/01_upload/main.js diff --git a/js/pages/02_baseline_landing_page/main.js b/js/views/02_baseline_landing_page/main.js similarity index 100% rename from js/pages/02_baseline_landing_page/main.js rename to js/views/02_baseline_landing_page/main.js diff --git a/js/pages/03_revenue/main.js b/js/views/03_revenue/main.js similarity index 100% rename from js/pages/03_revenue/main.js rename to js/views/03_revenue/main.js diff --git a/js/pages/04.5_OT/main.js b/js/views/04.5_OT/main.js similarity index 100% rename from js/pages/04.5_OT/main.js rename to js/views/04.5_OT/main.js diff --git a/js/pages/04_personnel/helpers.js b/js/views/04_personnel/helpers.js similarity index 100% rename from js/pages/04_personnel/helpers.js rename to js/views/04_personnel/helpers.js diff --git a/js/pages/04_personnel/main.js b/js/views/04_personnel/main.js similarity index 100% rename from js/pages/04_personnel/main.js rename to js/views/04_personnel/main.js diff --git a/js/pages/05_nonpersonnel/helpers.js b/js/views/05_nonpersonnel/helpers.js similarity index 100% rename from js/pages/05_nonpersonnel/helpers.js rename to js/views/05_nonpersonnel/helpers.js diff --git a/js/pages/05_nonpersonnel/main.js b/js/views/05_nonpersonnel/main.js similarity index 100% rename from js/pages/05_nonpersonnel/main.js rename to js/views/05_nonpersonnel/main.js diff --git a/js/pages/06_new_initiatives/helpers.js b/js/views/06_new_initiatives/helpers.js similarity index 100% rename from js/pages/06_new_initiatives/helpers.js rename to js/views/06_new_initiatives/helpers.js diff --git a/js/pages/06_new_initiatives/main.js b/js/views/06_new_initiatives/main.js similarity index 100% rename from js/pages/06_new_initiatives/main.js rename to js/views/06_new_initiatives/main.js diff --git a/js/pages/07_summary/main.js b/js/views/07_summary/main.js similarity index 100% rename from js/pages/07_summary/main.js rename to js/views/07_summary/main.js From 5b7ad15b90d3c5a9432f1c78bdde039df9cfb079 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 1 Jul 2024 13:21:56 -0400 Subject: [PATCH 15/30] removed archive --- archive/05_nonpersonnel.html | 177 ----------------------------------- 1 file changed, 177 deletions(-) delete mode 100644 archive/05_nonpersonnel.html diff --git a/archive/05_nonpersonnel.html b/archive/05_nonpersonnel.html deleted file mode 100644 index 45f36ce..0000000 --- a/archive/05_nonpersonnel.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - - -Demo Budget Form - - - - - - - - - - - - - - - - - - - - - - - - - -

FY2026 Budget Form

- -
-
- -
- - -
- -

Select an action item for each non-personnel line item in your department.

- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VendorAccount StringObjectRequest TotalAction
Law Firm LLC1000-00527-320010613100$100,000 -
- - - -
-
Office Supplier1000-00527-320010613100$10,000 -
- - - -
-
Software Co.1000-00527-320010613100$30,000 -
- - - -
-
-
-
-
- - -
-
-
- -
-
- - - - - - -
-
-
-
- - - -
-
- -
- - - \ No newline at end of file From 79921583b88c3f3bc9038758484da16d5bb372bd Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 1 Jul 2024 13:55:33 -0400 Subject: [PATCH 16/30] refactored form --- js/components/form/form.js | 108 +++---------------- js/components/form/subcomponents/dropdown.js | 25 +++++ js/components/form/subcomponents/fields.js | 53 +++++++++ js/components/form/subcomponents/submit.js | 26 +++++ js/components/table/subcomponents/cells.js | 4 +- js/utils/navigation-logic.js | 18 ++-- js/views/01_upload/main.js | 3 +- js/views/06_new_initiatives/helpers.js | 18 ++-- 8 files changed, 138 insertions(+), 117 deletions(-) create mode 100644 js/components/form/subcomponents/dropdown.js create mode 100644 js/components/form/subcomponents/fields.js create mode 100644 js/components/form/subcomponents/submit.js diff --git a/js/components/form/form.js b/js/components/form/form.js index 3e7620d..4cf9361 100644 --- a/js/components/form/form.js +++ b/js/components/form/form.js @@ -1,80 +1,8 @@ -// function to add questions to forms -// type is 'input' or 'textarea' -// inputType is for validation ('number' or 'text', etc) -function appendFormElement(type, label, inputId, required, inputType, form_id = 'new-form', cost = false) { +import Dropdown from "./subcomponents/dropdown.js"; +import NewField from "./subcomponents/fields.js"; +import SubmitButton from "./subcomponents/submit.js"; - // change if we want forms elsewhere - const form = document.getElementById(form_id); - - // create outer wrapper for element - const wrapper = document.createElement('div'); - - // label question - const labelEl = document.createElement('label'); - labelEl.textContent = label; - - // set type (input or textarea) - let inputEl; - if (type === 'input') { - inputEl = document.createElement('input'); - inputEl.type = inputType; - } else if (type === 'textarea') { - inputEl = document.createElement('textarea'); - } else { - throw new Error('Unsupported element type'); - } - - // mark as required if applicable - inputEl.required = required; - - // If an ID is provided, set it on the element - if (inputId) { - inputEl.id = inputId; - } - - // add elements - wrapper.appendChild(labelEl); - wrapper.appendChild(inputEl); - form.appendChild(wrapper); -} - - - -// Individual functions for each type of input. -export function addTextInput(label, inputId, required = false, form_id = 'new-form', cost = false) { - appendFormElement('input', label, inputId, required, 'text', form_id); -} - -export function addNumericInput(label, inputId, required = false, form_id = 'new-form', cost = true) { - appendFormElement('input', label, inputId, required, 'number', form_id); -} - -export function addTextarea(label, inputId, required = false, form_id = 'new-form', cost = false) { - appendFormElement('textarea', label, inputId, required, form_id); -} - -export function addSubmitButtonToForm(form_id = 'new-form') { - // Find the form by its ID - const form = document.getElementById(form_id); - - // Create the container `div` for the button - const buttonContainer = document.createElement('div'); - buttonContainer.id = 'submit-btn-container'; - - // Create the submit input - const submitInput = document.createElement('input'); - submitInput.className = 'btn btn-submit'; // Use appropriate class for your design - submitInput.type = 'submit'; - submitInput.value = 'Submit'; - - // Append the submit input to the container - buttonContainer.appendChild(submitInput); - - // Append the container to the form - form.appendChild(buttonContainer); -} - -export function fetchAllResponses(event) { +function fetchAllResponses(event) { event.preventDefault(); // Prevent the default form submission // Assuming `event.target` is the form itself @@ -102,7 +30,7 @@ export function fetchAllResponses(event) { return formData; } -export function addForm(element_id = 'modal-body', form_id = 'new-form') { +function addForm(element_id = 'modal-body', form_id = 'new-form') { const target_elem = document.getElementById(element_id); @@ -115,22 +43,12 @@ export function addForm(element_id = 'modal-body', form_id = 'new-form') { } -export async function createDropdownFromJSON(json_path) { - // Fetch JSON data from a file asynchronously - const response = await fetch(json_path); - const dataArray = await response.json(); - - // Creating a select element - const selectElement = document.createElement('select'); - - // Looping through the array and creating an option for each element - dataArray.forEach(item => { - const optionElement = document.createElement('option'); - optionElement.value = item.id; // Setting the option value to the item id - optionElement.textContent = item.name; // Setting the display text to the item name - selectElement.appendChild(optionElement); // Appending the option to the select - }); +export const Form = { + new : function(parent_elem_id) { addForm(parent_elem_id, 'new-form') }, + fetchAllResponses : function(event) { fetchAllResponses(event) }, + NewField : NewField, + Dropdown : Dropdown, + SubmitButton : SubmitButton +} - // Return the select element so it can be appended to the document - return selectElement; -} \ No newline at end of file +export default Form; \ No newline at end of file diff --git a/js/components/form/subcomponents/dropdown.js b/js/components/form/subcomponents/dropdown.js new file mode 100644 index 0000000..5f64600 --- /dev/null +++ b/js/components/form/subcomponents/dropdown.js @@ -0,0 +1,25 @@ +async function createDropdownFromJSON(json_path) { + // Fetch JSON data from a file asynchronously + const response = await fetch(json_path); + const dataArray = await response.json(); + + // Creating a select element + const selectElement = document.createElement('select'); + + // Looping through the array and creating an option for each element + dataArray.forEach(item => { + const optionElement = document.createElement('option'); + optionElement.value = item.id; // Setting the option value to the item id + optionElement.textContent = item.name; // Setting the display text to the item name + selectElement.appendChild(optionElement); // Appending the option to the select + }); + + // Return the select element so it can be appended to the document + return selectElement; +} + +export const Dropdown = { + createFromJSON : function(json_path){ return createDropdownFromJSON(json_path) } +} + +export default Dropdown; \ No newline at end of file diff --git a/js/components/form/subcomponents/fields.js b/js/components/form/subcomponents/fields.js new file mode 100644 index 0000000..1b65087 --- /dev/null +++ b/js/components/form/subcomponents/fields.js @@ -0,0 +1,53 @@ +// function to add questions to forms +// type is 'input' or 'textarea' +// inputType is for validation ('number' or 'text', etc) +function appendFormElement(type, label, inputId, required, inputType, form_id = 'new-form', cost = false) { + + // change if we want forms elsewhere + const form = document.getElementById(form_id); + + // create outer wrapper for element + const wrapper = document.createElement('div'); + + // label question + const labelEl = document.createElement('label'); + labelEl.textContent = label; + + // set type (input or textarea) + let inputEl; + if (type === 'input') { + inputEl = document.createElement('input'); + inputEl.type = inputType; + } else if (type === 'textarea') { + inputEl = document.createElement('textarea'); + } else { + throw new Error('Unsupported element type'); + } + + // mark as required if applicable + inputEl.required = required; + + // If an ID is provided, set it on the element + if (inputId) { + inputEl.id = inputId; + } + + // add elements + wrapper.appendChild(labelEl); + wrapper.appendChild(inputEl); + form.appendChild(wrapper); +} + +export const NewField = { + shortText : function(label, inputId, required = false, form_id = 'new-form', cost = false) { + appendFormElement('input', label, inputId, required, 'text', form_id); + }, + longText : function(label, inputId, required = false, form_id = 'new-form', cost = false) { + appendFormElement('textarea', label, inputId, required, form_id); + }, + numericInput: function(label, inputId, required = false, form_id = 'new-form', cost = true) { + appendFormElement('input', label, inputId, required, 'number', form_id); + } +} + +export default NewField; \ No newline at end of file diff --git a/js/components/form/subcomponents/submit.js b/js/components/form/subcomponents/submit.js new file mode 100644 index 0000000..3a47d55 --- /dev/null +++ b/js/components/form/subcomponents/submit.js @@ -0,0 +1,26 @@ +function addSubmitButtonToForm(form_id) { + // Find the form by its ID + const form = document.getElementById(form_id); + + // Create the container `div` for the button + const buttonContainer = document.createElement('div'); + buttonContainer.id = 'submit-btn-container'; + + // Create the submit input + const submitInput = document.createElement('input'); + submitInput.className = 'btn btn-submit'; // Use appropriate class for your design + submitInput.type = 'submit'; + submitInput.value = 'Submit'; + + // Append the submit input to the container + buttonContainer.appendChild(submitInput); + + // Append the container to the form + form.appendChild(buttonContainer); +} + +export const SubmitButton = { + add : function() { addSubmitButtonToForm('new-form') } +} + +export default SubmitButton; \ No newline at end of file diff --git a/js/components/table/subcomponents/cells.js b/js/components/table/subcomponents/cells.js index 5837408..1823a92 100644 --- a/js/components/table/subcomponents/cells.js +++ b/js/components/table/subcomponents/cells.js @@ -1,5 +1,5 @@ import { formatCurrency } from "../../../utils/utils.js"; -import { createDropdownFromJSON } from "../../form/form.js"; +import Dropdown from "../../form/subcomponents/dropdown.js"; // return cell value attribute or 0 if it does not exist function getCellValue(row, className) { @@ -30,7 +30,7 @@ async function createSelectCell(cellClass, json_filepath){ // get cell const cell = document.querySelector(`.active-editing td.${cellClass}`); // add service dropdown - const serviceDropdown = await createDropdownFromJSON(json_filepath); + const serviceDropdown = await Dropdown.createFromJSON(json_filepath); serviceDropdown.value = cell.textContent; // Clear the current content and append the textbox to the cell cell.innerHTML = ''; diff --git a/js/utils/navigation-logic.js b/js/utils/navigation-logic.js index 52a04e1..0745227 100644 --- a/js/utils/navigation-logic.js +++ b/js/utils/navigation-logic.js @@ -1,12 +1,12 @@ -import { initializeWelcomePage } from '../pages/00_welcome/main.js'; -import { loadNewInitiatives } from '../pages/06_new_initiatives/main.js' -import { loadRevenuePage } from '../pages/03_revenue/main.js' -import { loadPersonnelPage } from '../pages/04_personnel/main.js'; -import { loadOTPage } from '../pages/04.5_OT/main.js'; -import { loadNonpersonnelPage } from '../pages/05_nonpersonnel/main.js'; -import { loadBaselineLandingPage } from '../pages/02_baseline_landing_page/main.js'; -import { loadSummaryPage } from '../pages/07_summary/main.js'; -import { loadUploadPage } from '../pages/01_upload/main.js'; +import { initializeWelcomePage } from '../views/00_welcome/main.js'; +import { loadNewInitiatives } from '../views/06_new_initiatives/main.js' +import { loadRevenuePage } from '../views/03_revenue/main.js' +import { loadPersonnelPage } from '../views/04_personnel/main.js'; +import { loadOTPage } from '../views/04.5_OT/main.js'; +import { loadNonpersonnelPage } from '../views/05_nonpersonnel/main.js'; +import { loadBaselineLandingPage } from '../views/02_baseline_landing_page/main.js'; +import { loadSummaryPage } from '../views/07_summary/main.js'; +import { loadUploadPage } from '../views/01_upload/main.js'; import { loadPageState } from './storage-handlers.js'; diff --git a/js/views/01_upload/main.js b/js/views/01_upload/main.js index bfcbc02..19f1cf2 100644 --- a/js/views/01_upload/main.js +++ b/js/views/01_upload/main.js @@ -2,7 +2,6 @@ import { updatePageState } from "../../utils/storage-handlers.js"; import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' import NavButtons from '../../components/nav_buttons/nav_buttons.js' -import { initializeWelcomePage } from "../00_welcome/main.js"; import Body from "../../components/body/body.js"; export function loadUploadPage(){ @@ -18,6 +17,6 @@ export function loadUploadPage(){ // TODO: update to make dynamic Prompt.Text.update(`Placeholder for Excel Upload`); - Prompt.Buttons.Right.addAction(initializeWelcomePage) + ///Prompt.Buttons.Right.addAction(initializeWelcomePage) } \ No newline at end of file diff --git a/js/views/06_new_initiatives/helpers.js b/js/views/06_new_initiatives/helpers.js index 75c6917..7c01df9 100644 --- a/js/views/06_new_initiatives/helpers.js +++ b/js/views/06_new_initiatives/helpers.js @@ -2,7 +2,7 @@ import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' import { addModalLink, updateModalTitle, clearModal, hideModal } from '../../components/modal/modal.js' -import { fetchAllResponses, addTextarea, addTextInput, addNumericInput, addSubmitButtonToForm, addForm } from '../../components/form/form.js' +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' @@ -31,13 +31,13 @@ export function setUpModal() { export function setUpForm() { // Set up form - addForm(); - addTextInput('Initiative Name:', 'Initiative Name', true); // Add required field - addTextarea('Explain why this initiative is necessary and describe its potential impact.', 'Explanation', true); - addNumericInput('Estimate of ADDITONAL personnel cost?', 'Personnel Cost', true); - addNumericInput('Estimate of ADDITONAL nonpersonnel cost?', 'Non-personnel Cost', true); - addNumericInput('Estimate of TOTAL ADDITIONAL cost?', 'Total Cost', true); - addSubmitButtonToForm(); + Form.new('modal-body'); + Form.NewField.shortText('Initiative Name:', 'Initiative Name', true); + Form.NewField.longText('Explain why this initiative is necessary and describe its potential impact.', 'Explanation', true); + Form.NewField.numericInput('Estimate of ADDITONAL personnel cost?', 'Personnel Cost', true); + Form.NewField.numericInput('Estimate of ADDITONAL nonpersonnel cost?', 'Non-personnel Cost', true); + Form.NewField.numericInput('Estimate of TOTAL ADDITIONAL cost?', 'Total Cost', true); + Form.SubmitButton.add(); // Initialize form submission to table data handleFormSubmissions(); } @@ -59,7 +59,7 @@ export function handleFormSubmissions(event){ if (Object.values(responses)[0] != ''){ // change page view hideModal('main-modal'); - hidePrompt(); + Prompt.hide(); // add data to table Table.Rows.add(responses); From ec676ae9de9f351d595fc8583fc62d1530060237 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 1 Jul 2024 14:50:27 -0400 Subject: [PATCH 17/30] finished refactoring components --- js/components/body/body.js | 12 +++--- js/components/header/header.js | 10 +++-- js/components/modal/modal.js | 51 ++++++++++++++++------- js/components/welcome/welcome.js | 11 ++++- js/utils/navigation-logic.js | 12 +++++- js/views/00_welcome/helpers.js | 11 ++--- js/views/01_upload/helpers.js | 17 ++++++++ js/views/01_upload/main.js | 17 +------- js/views/02_baseline_landing_page/main.js | 4 +- js/views/03_revenue/main.js | 4 +- js/views/04.5_OT/main.js | 3 +- js/views/04_personnel/helpers.js | 4 +- js/views/05_nonpersonnel/helpers.js | 4 +- js/views/06_new_initiatives/helpers.js | 21 ++++++---- js/views/06_new_initiatives/main.js | 6 ++- js/views/07_summary/main.js | 4 +- 16 files changed, 122 insertions(+), 69 deletions(-) diff --git a/js/components/body/body.js b/js/components/body/body.js index b6d44dc..b001ca1 100644 --- a/js/components/body/body.js +++ b/js/components/body/body.js @@ -1,5 +1,5 @@ -import { hideWelcomeButtons } from '../../components/welcome/welcome.js' -import { hideModal, removeModalLink } from '../modal/modal.js'; +import Welcome from '../../components/welcome/welcome.js' +import Modal from '../modal/modal.js'; import NavButtons from '../nav_buttons/nav_buttons.js'; import Prompt from '../prompt/prompt.js'; import Sidebar from '../sidebar/sidebar.js'; @@ -8,15 +8,13 @@ import Table from '../table/table.js'; function clearAll() { // hide everything in the body - hideWelcomeButtons(); - hideModal(); + Welcome.hide(); + Modal.clear(); + Modal.hide(); NavButtons.hide(); Prompt.hide(); Table.hide(); Sidebar.hide(); - - // todo: fix this function to remove id in modal folder - removeModalLink('option1', 'main-modal'); } export const Body = { diff --git a/js/components/header/header.js b/js/components/header/header.js index d8e0e6f..8d12a1d 100644 --- a/js/components/header/header.js +++ b/js/components/header/header.js @@ -1,3 +1,7 @@ -export function updateSubtitle(subtitle){ - document.getElementById("subtitle").textContent = subtitle; -} \ No newline at end of file +export const Subtitle = { + update : function(subtitle){ + document.getElementById("subtitle").textContent = subtitle; + } +} + +export default Subtitle; \ No newline at end of file diff --git a/js/components/modal/modal.js b/js/components/modal/modal.js index a28814f..75f5f5e 100644 --- a/js/components/modal/modal.js +++ b/js/components/modal/modal.js @@ -1,28 +1,49 @@ -export function hideModal(modal_id) { + +function clearModal(){ + updateModalTitle(''); + document.getElementById('modal-body').innerHTML = ''; + //removeAllModalLinks() +} + +// function removeAllModalLinks(){ +// TODO +// } + +function hideModal(modal_id) { $('#' + modal_id).modal('hide'); } -export function showModal(modal_id) { +function showModal(modal_id) { $('#' + modal_id).modal('show'); } -export function addModalLink(button_id, modal_id){ - document.getElementById(button_id).addEventListener('click', function() { - showModal(modal_id); - }); +function showModalHandler() { + showModal('main-modal'); } -export function removeModalLink(button_id, modal_id){ - document.getElementById(button_id).removeEventListener('click', function() { - showModal(modal_id); - }); +const Link = { + add : function(button_id){ + document.getElementById(button_id).addEventListener('click', showModalHandler) + }, + remove : function(button_id){ + document.getElementById(button_id).removeEventListener('click', showModalHandler) + } } -export function updateModalTitle(title){ +function updateModalTitle(title) { document.getElementById('modal-title').textContent = title; } -export function clearModal(){ - updateModalTitle(''); - document.getElementById('modal-body').innerHTML = ''; -} \ No newline at end of file +const Title = { + update : function(title) { updateModalTitle(title) } +} + +export const Modal = { + hide : function() { hideModal('main-modal') }, + show : function() { showModal('main-modal') }, + clear : clearModal, + Title : Title, + Link : Link +} + +export default Modal; \ No newline at end of file diff --git a/js/components/welcome/welcome.js b/js/components/welcome/welcome.js index 2242300..e1a65d3 100644 --- a/js/components/welcome/welcome.js +++ b/js/components/welcome/welcome.js @@ -1,7 +1,14 @@ // Hide and unhide welcome buttons -export function unhideWelcomeButtons(){ +function unhideWelcomeButtons(){ document.getElementById("welcome-page").style.display = "block"; } -export function hideWelcomeButtons(){ +function hideWelcomeButtons(){ document.getElementById("welcome-page").style.display = "none"; } + +export const Welcome = { + show: unhideWelcomeButtons, + hide : hideWelcomeButtons +} + +export default Welcome; \ No newline at end of file diff --git a/js/utils/navigation-logic.js b/js/utils/navigation-logic.js index 0745227..2bc3210 100644 --- a/js/utils/navigation-logic.js +++ b/js/utils/navigation-logic.js @@ -1,5 +1,5 @@ import { initializeWelcomePage } from '../views/00_welcome/main.js'; -import { loadNewInitiatives } from '../views/06_new_initiatives/main.js' +import { cleanUpInitiativesPage, loadNewInitiatives } from '../views/06_new_initiatives/main.js' import { loadRevenuePage } from '../views/03_revenue/main.js' import { loadPersonnelPage } from '../views/04_personnel/main.js'; import { loadOTPage } from '../views/04.5_OT/main.js'; @@ -22,6 +22,10 @@ export let PAGES = { 'summary' : loadSummaryPage } +export let CLEANUP = { + 'new-inits' : cleanUpInitiativesPage, +} + export function nextPage(){ var page_state = loadPageState(); @@ -29,6 +33,9 @@ export function nextPage(){ // Find the index of the current key const currentIndex = keys.indexOf(page_state); + + // clean up current page + if (CLEANUP[page_state]) { CLEANUP[page_state]() }; // Check if there is a next key if (currentIndex >= 0 && currentIndex < keys.length - 1) { @@ -46,6 +53,9 @@ export function lastPage(){ // Find the index of the current key const currentIndex = keys.indexOf(page_state); + + // clean up current page + if (CLEANUP[page_state]) { CLEANUP[page_state]() }; // Check if there is a next key if (currentIndex >= 1) { diff --git a/js/views/00_welcome/helpers.js b/js/views/00_welcome/helpers.js index 58b6ce6..539fa4d 100644 --- a/js/views/00_welcome/helpers.js +++ b/js/views/00_welcome/helpers.js @@ -1,16 +1,17 @@ - import { updateSubtitle } from '../../components/header/header.js' -import { unhideWelcomeButtons } from '../../components/welcome/welcome.js' +import Subtitle from '../../components/header/header.js' +import Welcome from '../../components/welcome/welcome.js' +import Body from '../../components/body/body.js' + import { loadNewInitiatives } from '../06_new_initiatives/main.js' import { loadSummaryPage } from '../07_summary/main.js' import { loadBaselineLandingPage } from '../02_baseline_landing_page/main.js' import { loadUploadPage } from '../01_upload/main.js' -import Body from '../../components/body/body.js' export function initializePageView(){ // page set up Body.clearAll(); - updateSubtitle("Welcome"); - unhideWelcomeButtons(); + Subtitle.update("Welcome"); + Welcome.show(); } export function addLinks(){ diff --git a/js/views/01_upload/helpers.js b/js/views/01_upload/helpers.js index e69de29..c8a2587 100644 --- a/js/views/01_upload/helpers.js +++ b/js/views/01_upload/helpers.js @@ -0,0 +1,17 @@ +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"; + +function initializePageView() { + // prepare page view + Body.clearAll(); + NavButtons.show(); + + // update page text + Subtitle.update('Excel Upload'); + + // TODO: update to make dynamic + Prompt.Text.update(`Placeholder for Excel Upload`); + ///Prompt.Buttons.Right.addAction(initializeWelcomePage) +} diff --git a/js/views/01_upload/main.js b/js/views/01_upload/main.js index 19f1cf2..9a3236f 100644 --- a/js/views/01_upload/main.js +++ b/js/views/01_upload/main.js @@ -1,22 +1,9 @@ import { updatePageState } from "../../utils/storage-handlers.js"; -import { updateSubtitle } 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 { initializePageView } from "../06_new_initiatives/helpers.js"; export function loadUploadPage(){ //update page state updatePageState('upload'); - - // prepare page view - Body.clearAll(); - NavButtons.show(); - - // update page text - updateSubtitle('Excel Upload'); - - // TODO: update to make dynamic - Prompt.Text.update(`Placeholder for Excel Upload`); - ///Prompt.Buttons.Right.addAction(initializeWelcomePage) + initializePageView(); } \ No newline at end of file diff --git a/js/views/02_baseline_landing_page/main.js b/js/views/02_baseline_landing_page/main.js index ae5e121..5a28308 100644 --- a/js/views/02_baseline_landing_page/main.js +++ b/js/views/02_baseline_landing_page/main.js @@ -1,5 +1,5 @@ import { updatePageState } from "../../utils/storage-handlers.js"; -import { updateSubtitle } from '../../components/header/header.js' +import Subtitle from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' import NavButtons from '../../components/nav_buttons/nav_buttons.js' import Table from "../../components/table/table.js"; @@ -16,7 +16,7 @@ export function loadBaselineLandingPage(){ NavButtons.show(); // update page text - updateSubtitle('Baseline Budget Request'); + Subtitle.update('Baseline Budget Request'); // TODO: update to make dynamic Prompt.Text.update(`We will now ask you a series of questions about your BASELINE budget request. At the end, we will ask you about any new initiatives (ie. supplemental requests). diff --git a/js/views/03_revenue/main.js b/js/views/03_revenue/main.js index a0ccd7b..ccc5920 100644 --- a/js/views/03_revenue/main.js +++ b/js/views/03_revenue/main.js @@ -1,11 +1,11 @@ import { updatePageState } from '../../utils/storage-handlers.js' -import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' import { formatCurrency } from '../../utils/utils.js' import { REVENUE } from '../../init.js' import Body from '../../components/body/body.js' import NavButtons from '../../components/nav_buttons/nav_buttons.js' import { nextPage } from '../../utils/navigation-logic.js' +import Subtitle from '../../components/header/header.js' export function loadRevenuePage() { @@ -17,7 +17,7 @@ export function loadRevenuePage() { NavButtons.show(); // update page text - updateSubtitle('Revenue Projections'); + Subtitle.update('Revenue Projections'); // TODO: update to make dynamic Prompt.Text.update(`Your revenue projection for FY26 is ${formatCurrency(REVENUE, true)}`); Prompt.Buttons.Left.updateText('Confirm and continue.'); diff --git a/js/views/04.5_OT/main.js b/js/views/04.5_OT/main.js index 3fcf2dc..3dae704 100644 --- a/js/views/04.5_OT/main.js +++ b/js/views/04.5_OT/main.js @@ -1,4 +1,3 @@ -import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' import { updatePageState } from "../../utils/storage-handlers.js"; import Body from '../../components/body/body.js'; @@ -13,7 +12,7 @@ export function loadOTPage(){ NavButtons.show(); // update page text - updateSubtitle('Overtime Estimates'); + Subtitle.update('Overtime Estimates'); // TODO: update to make dynamic Prompt.Text.update(`This is a placeholder for the OT estimates.`); } \ No newline at end of file diff --git a/js/views/04_personnel/helpers.js b/js/views/04_personnel/helpers.js index daa5549..7ef8d99 100644 --- a/js/views/04_personnel/helpers.js +++ b/js/views/04_personnel/helpers.js @@ -1,11 +1,11 @@ import Prompt from "../../components/prompt/prompt.js"; -import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import Table from '../../components/table/table.js' import Sidebar from "../../components/sidebar/sidebar.js"; import { DATA_ROOT, fringe, cola, merit } from "../../init.js" import Body from "../../components/body/body.js"; import NavButtons from "../../components/nav_buttons/nav_buttons.js"; +import Subtitle from "../../components/header/header.js"; const personnelColumns = [ { title: 'Job Name', className: 'job-name' }, @@ -25,7 +25,7 @@ export function preparePageView(){ Table.adjustWidth('90%'); // update page text - updateSubtitle('Personnel'); + Subtitle.update('Personnel'); Prompt.Text.update('For each job in your department, select the service and request the number of baseline and supplemental FTEs.'); } diff --git a/js/views/05_nonpersonnel/helpers.js b/js/views/05_nonpersonnel/helpers.js index b6d6234..8832894 100644 --- a/js/views/05_nonpersonnel/helpers.js +++ b/js/views/05_nonpersonnel/helpers.js @@ -1,11 +1,11 @@ import Prompt from "../../components/prompt/prompt.js"; import Sidebar from "../../components/sidebar/sidebar.js"; import Table from "../../components/table/table.js"; -import { updateSubtitle } from "../../components/header/header.js"; import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; import Body from "../../components/body/body.js"; import NavButtons from "../../components/nav_buttons/nav_buttons.js"; +import Subtitle from "../../components/header/header.js"; const nonPersonnelColumns = [ { title: 'Request Total', className: 'request', isCost: true }, @@ -18,7 +18,7 @@ export function preparePageView(){ Sidebar.show(); Table.adjustWidth('100%'); // update page text - updateSubtitle('Non-Personnel'); + Subtitle.update('Non-Personnel'); Prompt.Text.update('Select an action item for each non-personnel line item from last year.'); updateDisplayandTotals(); } diff --git a/js/views/06_new_initiatives/helpers.js b/js/views/06_new_initiatives/helpers.js index 7c01df9..0bc2515 100644 --- a/js/views/06_new_initiatives/helpers.js +++ b/js/views/06_new_initiatives/helpers.js @@ -1,12 +1,12 @@ -import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' -import { addModalLink, updateModalTitle, clearModal, hideModal } from '../../components/modal/modal.js' +import Modal from '../../components/modal/modal.js' 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 { nextPage } from '../../utils/navigation-logic.js' +import Subtitle from '../../components/header/header.js' export function initializePageView() { // Prepare page view @@ -15,7 +15,7 @@ export function initializePageView() { Prompt.Buttons.Right.addAction(nextPage); // Load text - updateSubtitle('New Initiatives'); + Subtitle.update('New Initiatives'); Prompt.Text.update('Do you have any new initiatives for FY26?'); Prompt.Buttons.Left.updateText('Yes'); Prompt.Buttons.Right.updateText('No'); @@ -23,10 +23,10 @@ export function initializePageView() { export function setUpModal() { // Initialize modal - clearModal(); - addModalLink('option1', 'main-modal'); - updateModalTitle('New initiative'); - addModalLink('add-btn', 'main-modal'); + Modal.clear(); + Modal.Link.add('option1'); + Modal.Title.update('New initiative'); + Modal.Link.add('add-btn'); } export function setUpForm() { @@ -58,7 +58,7 @@ export function handleFormSubmissions(event){ // make sure it's not an empty response if (Object.values(responses)[0] != ''){ // change page view - hideModal('main-modal'); + Modal.hide(); Prompt.hide(); // add data to table @@ -71,3 +71,8 @@ export function handleFormSubmissions(event){ }) } + +export function removeModalLinks(){ + Modal.Link.remove('option1'); + Modal.Link.remove('add-btn'); +} \ 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 1ade087..bf9c1b0 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 } from './helpers.js' +import { initializePageView, setUpModal, setUpForm, setUpTable, removeModalLinks } from './helpers.js' import { updatePageState } from '../../utils/storage-handlers.js' @@ -11,3 +11,7 @@ export function loadNewInitiatives() { setUpForm(); setUpTable(); } + +export function cleanUpInitiativesPage() { + removeModalLinks(); +} \ No newline at end of file diff --git a/js/views/07_summary/main.js b/js/views/07_summary/main.js index e29a8bd..759f80b 100644 --- a/js/views/07_summary/main.js +++ b/js/views/07_summary/main.js @@ -1,8 +1,8 @@ import { updatePageState } from "../../utils/storage-handlers.js"; -import { updateSubtitle } from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' import { initializeWelcomePage } from "../00_welcome/main.js"; import Body from "../../components/body/body.js"; +import Subtitle from "../../components/header/header.js"; export function loadSummaryPage(){ //update page state @@ -14,7 +14,7 @@ export function loadSummaryPage(){ Prompt.Buttons.Right.updateText('Go back and edit'); // update page text - updateSubtitle('Summary'); + Subtitle.update('Summary'); // TODO: update to make dynamic Prompt.Text.update(`Placeholder for summary and any issues.`); Prompt.Buttons.Right.addAction(initializeWelcomePage); From ba74214b582f4f0df8215aea73d3210454810dac Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 1 Jul 2024 15:33:05 -0400 Subject: [PATCH 18/30] reorg utils adn views --- js/components/nav_buttons/nav_buttons.js | 2 +- js/components/sidebar/sidebar.js | 2 +- js/components/table/subcomponents/cells.js | 2 +- js/components/table/subcomponents/columns.js | 2 +- js/components/table/subcomponents/data.js | 44 +++++++++ js/components/table/table.js | 5 +- js/init.js | 4 +- js/utils/archive/archived_fns.js | 91 +++++++++++++++++++ js/utils/{utils.js => common_utils.js} | 0 js/utils/data-handlers.js | 47 ---------- js/utils/data_utils/JSON_data_handlers.js | 12 +++ .../excel_export.js} | 0 .../local_storage_handlers.js} | 0 js/views/00_welcome/main.js | 2 +- js/views/01_upload/main.js | 2 +- js/views/02_baseline_landing_page/main.js | 5 +- js/views/03_revenue/main.js | 6 +- js/views/04.5_OT/main.js | 2 +- js/views/04_personnel/helpers.js | 3 +- js/views/04_personnel/main.js | 2 +- js/views/05_nonpersonnel/helpers.js | 3 +- js/views/05_nonpersonnel/main.js | 2 +- js/views/06_new_initiatives/helpers.js | 2 +- js/views/06_new_initiatives/main.js | 2 +- js/views/07_summary/main.js | 2 +- .../view_logic.js} | 20 ++-- 26 files changed, 181 insertions(+), 83 deletions(-) create mode 100644 js/components/table/subcomponents/data.js create mode 100644 js/utils/archive/archived_fns.js rename js/utils/{utils.js => common_utils.js} (100%) delete mode 100644 js/utils/data-handlers.js create mode 100644 js/utils/data_utils/JSON_data_handlers.js rename js/utils/{excel-export.js => data_utils/excel_export.js} (100%) rename js/utils/{storage-handlers.js => data_utils/local_storage_handlers.js} (100%) rename js/{utils/navigation-logic.js => views/view_logic.js} (67%) diff --git a/js/components/nav_buttons/nav_buttons.js b/js/components/nav_buttons/nav_buttons.js index ff85a8f..fadc302 100644 --- a/js/components/nav_buttons/nav_buttons.js +++ b/js/components/nav_buttons/nav_buttons.js @@ -1,4 +1,4 @@ -import { nextPage, lastPage } from '../../utils/navigation-logic.js' +import { nextPage, lastPage } from '../../views/view_logic.js' function initializeNavButtons(){ // initialize last button diff --git a/js/components/sidebar/sidebar.js b/js/components/sidebar/sidebar.js index 4563e69..4b3da7d 100644 --- a/js/components/sidebar/sidebar.js +++ b/js/components/sidebar/sidebar.js @@ -1,4 +1,4 @@ -import { formatCurrency } from "../../utils/utils.js"; +import { formatCurrency } from "../../utils/common_utils.js"; import { TARGET } from "../../init.js"; function hideSidebar(){ diff --git a/js/components/table/subcomponents/cells.js b/js/components/table/subcomponents/cells.js index 1823a92..1e229aa 100644 --- a/js/components/table/subcomponents/cells.js +++ b/js/components/table/subcomponents/cells.js @@ -1,4 +1,4 @@ -import { formatCurrency } from "../../../utils/utils.js"; +import { formatCurrency } from "../../../utils/common_utils.js"; import Dropdown from "../../form/subcomponents/dropdown.js"; // return cell value attribute or 0 if it does not exist diff --git a/js/components/table/subcomponents/columns.js b/js/components/table/subcomponents/columns.js index fbbde3f..02faf57 100644 --- a/js/components/table/subcomponents/columns.js +++ b/js/components/table/subcomponents/columns.js @@ -1,4 +1,4 @@ -import { formatCurrency } from "../../../utils/utils.js"; +import { formatCurrency } from "../../../utils/common_utils.js"; // position is index at which new column will be inserted function addCol(position, htmlContent = '', headerTitle = '') { diff --git a/js/components/table/subcomponents/data.js b/js/components/table/subcomponents/data.js new file mode 100644 index 0000000..b19f842 --- /dev/null +++ b/js/components/table/subcomponents/data.js @@ -0,0 +1,44 @@ +import { fetchJSON } from "../../../utils/data_utils/JSON_data_handlers.js"; + +function loadJSONIntoTable(jsonFilePath) { + fetchJSON(jsonFilePath) + .then(data => { + if(Array.isArray(data)) { + const table = document.getElementById('main-table'); + const thead = table.querySelector('thead'); + const tbody = table.querySelector('tbody'); + + // 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); + }); + tbody.appendChild(row); + }); + + } else { + console.error('The provided JSON file does not contain an array of objects.'); + } + }) + .catch(error => { + console.error('Failed to load and parse the JSON file:', error); + }); + } + +export const Data = { + loadFromJSON : function(jsonFilePath) { loadJSONIntoTable(jsonFilePath) } +} + +export default Data; \ No newline at end of file diff --git a/js/components/table/table.js b/js/components/table/table.js index 372ea49..4e3a8b7 100644 --- a/js/components/table/table.js +++ b/js/components/table/table.js @@ -3,6 +3,7 @@ import Cell from './subcomponents/cells.js' import Columns from './subcomponents/columns.js' import Header from './subcomponents/headers.js' import Rows from './subcomponents/rows.js' +import Data from './subcomponents/data.js' function adjustTableWidth(width_pct){ const table = document.getElementById('main-table'); @@ -12,8 +13,7 @@ function adjustTableWidth(width_pct){ function clearTable(){ const table = document.getElementById('main-table'); table.querySelector('thead').innerHTML = ''; - table.querySelector('tbody'). - innerHTML = ''; + table.querySelector('tbody').innerHTML = ''; } function showTable(){ @@ -33,6 +33,7 @@ const Table = { Columns : Columns, Header : Header, Rows : Rows, + Data : Data, // functions adjustWidth : function(width_pct){ adjustTableWidth(width_pct) diff --git a/js/init.js b/js/init.js index 0ba40cb..ed1d4a6 100644 --- a/js/init.js +++ b/js/init.js @@ -1,6 +1,6 @@ // import functions -import { loadPageState } from './utils/storage-handlers.js' -import { PAGES } from './utils/navigation-logic.js' +import { loadPageState } from './utils/data_utils/local_storage_handlers.js' +import { PAGES } from './views/view_logic.js' // path for my laptop export let DATA_ROOT = '../../../data/law_dept_sample/' diff --git a/js/utils/archive/archived_fns.js b/js/utils/archive/archived_fns.js new file mode 100644 index 0000000..2450097 --- /dev/null +++ b/js/utils/archive/archived_fns.js @@ -0,0 +1,91 @@ +/** + * Transforms a specified cell into an editable element by attaching an input field. + * Once the editing is committed, the new value is saved in the specified attribute + * of the element and passed through an optional formatting function before being + * displayed in the cell. An optional callback can be triggered after the update + * to perform additional actions. + */ +function createEditableCell(cell, attribute = 'value', formatValueCallback, updateCallback, validate) { + // Add a click event to the cell to make it editable + cell.onclick = function() { + // Fetch the current attribute value of the cell or fall back to an empty string + var currentValue = cell.getAttribute(attribute) || ''; + // Create an input element to edit the value + var textbox = document.createElement('input'); + textbox.type = 'text'; + textbox.value = currentValue; + var feedback = document.createElement('p'); + feedback.style.color = "red"; + + // Function to commit the textbox value and restore static text + function commitAndRestoreText() { + // Retrieve the entered value + var enteredValue = textbox.value; + // Set the attribute to the entered value + cell.setAttribute(attribute, enteredValue); + + // validate text against validation criteria + let feedback_text = ''; + if (validate){ + feedback_text = validate(enteredValue); + } + + // if there's an error, show it + if (feedback_text){ + feedback.textContent = feedback_text; + // otherwise, proceed + } else { + // Format and set the cell's text content + cell.textContent = formatValueCallback ? formatValueCallback(enteredValue) : enteredValue; + // If there is an update callback provided, call it + if (updateCallback) { + updateCallback(); + } + }; + + // Reattach the onclick event to allow editing again in the future + cell.onclick = function() { + createEditableCell(cell, attribute, formatValueCallback, updateCallback, validate); + }; + } + + // When the textbox loses focus, commit its value + textbox.onblur = commitAndRestoreText; + // When the user presses the 'Enter' key, commit the value and blur the textbox + textbox.onkeydown = function(event) { + if (event.key === 'Enter') { + commitAndRestoreText(); + textbox.blur(); + } + }; + + // Clear the current content and append the textbox to the cell + cell.innerHTML = ''; + cell.appendChild(textbox); + cell.appendChild(feedback); + // Temporarily remove the onclick event handler to prevent re-triggering during edit + cell.onclick = null; + + // Focus on the textbox to start editing + textbox.focus(); + } +} + +// Function to apply createEditableCell to all cells matching a given selector +function applyEditableCells(selector, attribute = 'value', formatValueCallback, updateCallback, validate) { + // Select all elements that match the provided selector + var cells = document.querySelectorAll(selector); + // Iterate over each cell and make it editable + cells.forEach(function(cell) { + createEditableCell(cell, attribute, formatValueCallback, updateCallback, validate); + }); +} + +function validateNumber(input){ + var number = parseFloat(input); + if (isNaN(number)){ + return "Field only accepts numbers"; + }; + return ""; +} + diff --git a/js/utils/utils.js b/js/utils/common_utils.js similarity index 100% rename from js/utils/utils.js rename to js/utils/common_utils.js diff --git a/js/utils/data-handlers.js b/js/utils/data-handlers.js deleted file mode 100644 index 525dc1b..0000000 --- a/js/utils/data-handlers.js +++ /dev/null @@ -1,47 +0,0 @@ -export function loadJSONIntoTable(jsonFilePath) { - return fetch(jsonFilePath) - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); - }) - .then(data => { - if(Array.isArray(data)) { - const table = document.getElementById('main-table'); - const thead = table.querySelector('thead'); - const tbody = table.querySelector('tbody'); - - // Clear any existing content - 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); - }); - tbody.appendChild(row); - }); - - } else { - console.error('The provided JSON file does not contain an array of objects.'); - } - }) - .catch(error => { - console.error('Failed to load and parse the JSON file:', error); - }); - } - \ No newline at end of file diff --git a/js/utils/data_utils/JSON_data_handlers.js b/js/utils/data_utils/JSON_data_handlers.js new file mode 100644 index 0000000..ca8211e --- /dev/null +++ b/js/utils/data_utils/JSON_data_handlers.js @@ -0,0 +1,12 @@ +export function fetchJSON(jsonFilePath) { + return fetch(jsonFilePath) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }); +} + + + \ No newline at end of file diff --git a/js/utils/excel-export.js b/js/utils/data_utils/excel_export.js similarity index 100% rename from js/utils/excel-export.js rename to js/utils/data_utils/excel_export.js diff --git a/js/utils/storage-handlers.js b/js/utils/data_utils/local_storage_handlers.js similarity index 100% rename from js/utils/storage-handlers.js rename to js/utils/data_utils/local_storage_handlers.js diff --git a/js/views/00_welcome/main.js b/js/views/00_welcome/main.js index 401addc..49441b1 100644 --- a/js/views/00_welcome/main.js +++ b/js/views/00_welcome/main.js @@ -1,5 +1,5 @@ -import { updatePageState } from '../../utils/storage-handlers.js' +import { updatePageState } from '../../utils/data_utils/local_storage_handlers.js' import { initializePageView, addLinks } from './helpers.js' export function initializeWelcomePage(){ diff --git a/js/views/01_upload/main.js b/js/views/01_upload/main.js index 9a3236f..a082d44 100644 --- a/js/views/01_upload/main.js +++ b/js/views/01_upload/main.js @@ -1,4 +1,4 @@ -import { updatePageState } from "../../utils/storage-handlers.js"; +import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; import { initializePageView } from "../06_new_initiatives/helpers.js"; export function loadUploadPage(){ diff --git a/js/views/02_baseline_landing_page/main.js b/js/views/02_baseline_landing_page/main.js index 5a28308..7a40b3a 100644 --- a/js/views/02_baseline_landing_page/main.js +++ b/js/views/02_baseline_landing_page/main.js @@ -1,9 +1,8 @@ -import { updatePageState } from "../../utils/storage-handlers.js"; +import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; import Subtitle from '../../components/header/header.js' import Prompt from '../../components/prompt/prompt.js' import NavButtons from '../../components/nav_buttons/nav_buttons.js' import Table from "../../components/table/table.js"; -import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; import Body from "../../components/body/body.js"; @@ -22,7 +21,7 @@ export function loadBaselineLandingPage(){ At the end, we will ask you about any new initiatives (ie. supplemental requests). Select one of your funds to begin.`); - loadJSONIntoTable(DATA_ROOT + 'funds.json', 'main-table') + Table.Data.loadFromJSON(DATA_ROOT + 'funds.json') Table.adjustWidth('100%'); Table.show(); } \ No newline at end of file diff --git a/js/views/03_revenue/main.js b/js/views/03_revenue/main.js index ccc5920..e5be62a 100644 --- a/js/views/03_revenue/main.js +++ b/js/views/03_revenue/main.js @@ -1,10 +1,10 @@ -import { updatePageState } from '../../utils/storage-handlers.js' +import { updatePageState } from '../../utils/data_utils/local_storage_handlers.js' import Prompt from '../../components/prompt/prompt.js' -import { formatCurrency } from '../../utils/utils.js' +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 { nextPage } from '../../utils/navigation-logic.js' +import { nextPage } from '../view_logic.js' import Subtitle from '../../components/header/header.js' export function loadRevenuePage() { diff --git a/js/views/04.5_OT/main.js b/js/views/04.5_OT/main.js index 3dae704..0285d1e 100644 --- a/js/views/04.5_OT/main.js +++ b/js/views/04.5_OT/main.js @@ -1,5 +1,5 @@ import Prompt from '../../components/prompt/prompt.js' -import { updatePageState } from "../../utils/storage-handlers.js"; +import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; import Body from '../../components/body/body.js'; import NavButtons from '../../components/nav_buttons/nav_buttons.js'; diff --git a/js/views/04_personnel/helpers.js b/js/views/04_personnel/helpers.js index 7ef8d99..5482781 100644 --- a/js/views/04_personnel/helpers.js +++ b/js/views/04_personnel/helpers.js @@ -1,5 +1,4 @@ import Prompt from "../../components/prompt/prompt.js"; -import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import Table from '../../components/table/table.js' import Sidebar from "../../components/sidebar/sidebar.js"; import { DATA_ROOT, fringe, cola, merit } from "../../init.js" @@ -37,7 +36,7 @@ function personnelRowOnEdit(){ export async function initializePersonnelTable(){ // load table data from json - await loadJSONIntoTable(DATA_ROOT + 'personnel_data.json'); + await Table.Data.loadFromJSON(DATA_ROOT + 'personnel_data.json'); //after table is loaded, fill it Table.show(); Table.Columns.add(3, '', 'Service'); diff --git a/js/views/04_personnel/main.js b/js/views/04_personnel/main.js index c067081..581a93d 100644 --- a/js/views/04_personnel/main.js +++ b/js/views/04_personnel/main.js @@ -1,5 +1,5 @@ -import { updatePageState } from "../../utils/storage-handlers.js"; +import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; import { preparePageView, initializePersonnelTable } from "./helpers.js"; export function loadPersonnelPage(){ diff --git a/js/views/05_nonpersonnel/helpers.js b/js/views/05_nonpersonnel/helpers.js index 8832894..7793b3c 100644 --- a/js/views/05_nonpersonnel/helpers.js +++ b/js/views/05_nonpersonnel/helpers.js @@ -1,7 +1,6 @@ import Prompt from "../../components/prompt/prompt.js"; import Sidebar from "../../components/sidebar/sidebar.js"; import Table from "../../components/table/table.js"; -import { loadJSONIntoTable } from "../../utils/data-handlers.js"; import { DATA_ROOT } from "../../init.js"; import Body from "../../components/body/body.js"; import NavButtons from "../../components/nav_buttons/nav_buttons.js"; @@ -25,7 +24,7 @@ export function preparePageView(){ export async function initializeNonpersonnelTable(){ // load table data from json - await loadJSONIntoTable(DATA_ROOT + 'nonpersonnel_data.json', 'main-table'); + await Table.Data.loadFromJSON(DATA_ROOT + 'nonpersonnel_data.json', 'main-table'); //after table is loaded, fill it Table.show(); Table.Columns.addAtEnd(Table.Buttons.all_btns, "Select Action"); diff --git a/js/views/05_nonpersonnel/main.js b/js/views/05_nonpersonnel/main.js index 6e2e72f..566cf77 100644 --- a/js/views/05_nonpersonnel/main.js +++ b/js/views/05_nonpersonnel/main.js @@ -1,4 +1,4 @@ -import { updatePageState } from "../../utils/storage-handlers.js"; +import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; import { preparePageView, initializeNonpersonnelTable } from "../05_nonpersonnel/helpers.js"; export function loadNonpersonnelPage(){ diff --git a/js/views/06_new_initiatives/helpers.js b/js/views/06_new_initiatives/helpers.js index 0bc2515..eed514a 100644 --- a/js/views/06_new_initiatives/helpers.js +++ b/js/views/06_new_initiatives/helpers.js @@ -5,7 +5,7 @@ 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 { nextPage } from '../../utils/navigation-logic.js' +import { nextPage } from '../view_logic.js' import Subtitle from '../../components/header/header.js' export function initializePageView() { diff --git a/js/views/06_new_initiatives/main.js b/js/views/06_new_initiatives/main.js index bf9c1b0..94e84b0 100644 --- a/js/views/06_new_initiatives/main.js +++ b/js/views/06_new_initiatives/main.js @@ -1,6 +1,6 @@ import { initializePageView, setUpModal, setUpForm, setUpTable, removeModalLinks } from './helpers.js' -import { updatePageState } from '../../utils/storage-handlers.js' +import { updatePageState } from '../../utils/data_utils/local_storage_handlers.js' // set up page and initialize all buttons diff --git a/js/views/07_summary/main.js b/js/views/07_summary/main.js index 759f80b..97ac09b 100644 --- a/js/views/07_summary/main.js +++ b/js/views/07_summary/main.js @@ -1,4 +1,4 @@ -import { updatePageState } from "../../utils/storage-handlers.js"; +import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; import Prompt from '../../components/prompt/prompt.js' import { initializeWelcomePage } from "../00_welcome/main.js"; import Body from "../../components/body/body.js"; diff --git a/js/utils/navigation-logic.js b/js/views/view_logic.js similarity index 67% rename from js/utils/navigation-logic.js rename to js/views/view_logic.js index 2bc3210..8b4dfa8 100644 --- a/js/utils/navigation-logic.js +++ b/js/views/view_logic.js @@ -1,14 +1,14 @@ -import { initializeWelcomePage } from '../views/00_welcome/main.js'; -import { cleanUpInitiativesPage, loadNewInitiatives } from '../views/06_new_initiatives/main.js' -import { loadRevenuePage } from '../views/03_revenue/main.js' -import { loadPersonnelPage } from '../views/04_personnel/main.js'; -import { loadOTPage } from '../views/04.5_OT/main.js'; -import { loadNonpersonnelPage } from '../views/05_nonpersonnel/main.js'; -import { loadBaselineLandingPage } from '../views/02_baseline_landing_page/main.js'; -import { loadSummaryPage } from '../views/07_summary/main.js'; -import { loadUploadPage } from '../views/01_upload/main.js'; +import { initializeWelcomePage } from './00_welcome/main.js'; +import { cleanUpInitiativesPage, loadNewInitiatives } from './06_new_initiatives/main.js' +import { loadRevenuePage } from './03_revenue/main.js' +import { loadPersonnelPage } from './04_personnel/main.js'; +import { loadOTPage } from './04.5_OT/main.js'; +import { loadNonpersonnelPage } from './05_nonpersonnel/main.js'; +import { loadBaselineLandingPage } from './02_baseline_landing_page/main.js'; +import { loadSummaryPage } from './07_summary/main.js'; +import { loadUploadPage } from './01_upload/main.js'; -import { loadPageState } from './storage-handlers.js'; +import { loadPageState } from '../utils/data_utils/local_storage_handlers.js'; export let PAGES = { 'welcome' : initializeWelcomePage, From 9f4fa7743f78fba5ea2ec259c05cbb80904a049e Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 1 Jul 2024 16:11:36 -0400 Subject: [PATCH 19/30] fixed bug with async functions --- js/components/table/subcomponents/data.js | 25 ++++--- js/utils/common_utils.js | 91 ----------------------- js/utils/data_utils/JSON_data_handlers.js | 2 +- js/views/04.5_OT/main.js | 1 + 4 files changed, 16 insertions(+), 103 deletions(-) diff --git a/js/components/table/subcomponents/data.js b/js/components/table/subcomponents/data.js index b19f842..4c50d3c 100644 --- a/js/components/table/subcomponents/data.js +++ b/js/components/table/subcomponents/data.js @@ -1,12 +1,16 @@ import { fetchJSON } from "../../../utils/data_utils/JSON_data_handlers.js"; -function loadJSONIntoTable(jsonFilePath) { - fetchJSON(jsonFilePath) - .then(data => { - if(Array.isArray(data)) { +async function loadJSONIntoTable(jsonFilePath) { + const data = await fetchJSON(jsonFilePath); + 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'); @@ -28,17 +32,16 @@ function loadJSONIntoTable(jsonFilePath) { tbody.appendChild(row); }); - } else { + } else { console.error('The provided JSON file does not contain an array of objects.'); - } - }) - .catch(error => { - console.error('Failed to load and parse the JSON file:', error); - }); + } + } catch(error) { + console.error('Failed to load and parse the JSON file:', error); } +} export const Data = { - loadFromJSON : function(jsonFilePath) { loadJSONIntoTable(jsonFilePath) } + loadFromJSON : loadJSONIntoTable } export default Data; \ No newline at end of file diff --git a/js/utils/common_utils.js b/js/utils/common_utils.js index 6c05a7e..082f22a 100644 --- a/js/utils/common_utils.js +++ b/js/utils/common_utils.js @@ -22,94 +22,3 @@ const unformatCurrency = (formattedAmount) => { return parseFloat(numericalPart); }; -/** - * Transforms a specified cell into an editable element by attaching an input field. - * Once the editing is committed, the new value is saved in the specified attribute - * of the element and passed through an optional formatting function before being - * displayed in the cell. An optional callback can be triggered after the update - * to perform additional actions. - */ -function createEditableCell(cell, attribute = 'value', formatValueCallback, updateCallback, validate) { - // Add a click event to the cell to make it editable - cell.onclick = function() { - // Fetch the current attribute value of the cell or fall back to an empty string - var currentValue = cell.getAttribute(attribute) || ''; - // Create an input element to edit the value - var textbox = document.createElement('input'); - textbox.type = 'text'; - textbox.value = currentValue; - var feedback = document.createElement('p'); - feedback.style.color = "red"; - - // Function to commit the textbox value and restore static text - function commitAndRestoreText() { - // Retrieve the entered value - var enteredValue = textbox.value; - // Set the attribute to the entered value - cell.setAttribute(attribute, enteredValue); - - // validate text against validation criteria - let feedback_text = ''; - if (validate){ - feedback_text = validate(enteredValue); - } - - // if there's an error, show it - if (feedback_text){ - feedback.textContent = feedback_text; - // otherwise, proceed - } else { - // Format and set the cell's text content - cell.textContent = formatValueCallback ? formatValueCallback(enteredValue) : enteredValue; - // If there is an update callback provided, call it - if (updateCallback) { - updateCallback(); - } - }; - - // Reattach the onclick event to allow editing again in the future - cell.onclick = function() { - createEditableCell(cell, attribute, formatValueCallback, updateCallback, validate); - }; - } - - // When the textbox loses focus, commit its value - textbox.onblur = commitAndRestoreText; - // When the user presses the 'Enter' key, commit the value and blur the textbox - textbox.onkeydown = function(event) { - if (event.key === 'Enter') { - commitAndRestoreText(); - textbox.blur(); - } - }; - - // Clear the current content and append the textbox to the cell - cell.innerHTML = ''; - cell.appendChild(textbox); - cell.appendChild(feedback); - // Temporarily remove the onclick event handler to prevent re-triggering during edit - cell.onclick = null; - - // Focus on the textbox to start editing - textbox.focus(); - } -} - -// Function to apply createEditableCell to all cells matching a given selector -function applyEditableCells(selector, attribute = 'value', formatValueCallback, updateCallback, validate) { - // Select all elements that match the provided selector - var cells = document.querySelectorAll(selector); - // Iterate over each cell and make it editable - cells.forEach(function(cell) { - createEditableCell(cell, attribute, formatValueCallback, updateCallback, validate); - }); -} - -function validateNumber(input){ - var number = parseFloat(input); - if (isNaN(number)){ - return "Field only accepts numbers"; - }; - return ""; -} - diff --git a/js/utils/data_utils/JSON_data_handlers.js b/js/utils/data_utils/JSON_data_handlers.js index ca8211e..205a97a 100644 --- a/js/utils/data_utils/JSON_data_handlers.js +++ b/js/utils/data_utils/JSON_data_handlers.js @@ -1,4 +1,4 @@ -export function fetchJSON(jsonFilePath) { +export async function fetchJSON(jsonFilePath) { return fetch(jsonFilePath) .then(response => { if (!response.ok) { diff --git a/js/views/04.5_OT/main.js b/js/views/04.5_OT/main.js index 0285d1e..1181a30 100644 --- a/js/views/04.5_OT/main.js +++ b/js/views/04.5_OT/main.js @@ -2,6 +2,7 @@ import Prompt from '../../components/prompt/prompt.js' import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; import Body from '../../components/body/body.js'; import NavButtons from '../../components/nav_buttons/nav_buttons.js'; +import Subtitle from '../../components/header/header.js'; export function loadOTPage(){ //update page state From 7f2c794d94152cb9842fd75a502de804938cb33e Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Tue, 2 Jul 2024 08:50:12 -0400 Subject: [PATCH 20/30] support to save fund --- css/common.css | 1 + js/components/header/header.js | 5 ++ js/components/table/table.css | 9 +++ js/views/02_baseline_landing_page/helpers.js | 58 ++++++++++++++++++++ js/views/02_baseline_landing_page/main.js | 27 ++------- js/views/04.5_OT/main.js | 2 + 6 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 js/views/02_baseline_landing_page/helpers.js diff --git a/css/common.css b/css/common.css index 5c0da89..28dc6da 100644 --- a/css/common.css +++ b/css/common.css @@ -11,6 +11,7 @@ --citygreen: #004445; --spiritgreen: #279989; --palegreen: #9FD5B3; + --verypalegreen: #DFF5E3; --accentyellow: #feb70d; --nearblack: #18252a; font-family: 'Clear Sans', 'Helvetica Neue', Arial, sans-serif; diff --git a/js/components/header/header.js b/js/components/header/header.js index 8d12a1d..825c48c 100644 --- a/js/components/header/header.js +++ b/js/components/header/header.js @@ -1,5 +1,10 @@ export const Subtitle = { update : function(subtitle){ + // get current fund + var fund = localStorage.getItem("fund"); + if (fund){ + var subtitle = `${subtitle}: ${fund}`; + } document.getElementById("subtitle").textContent = subtitle; } } diff --git a/js/components/table/table.css b/js/components/table/table.css index bad8e1d..61d69f7 100644 --- a/js/components/table/table.css +++ b/js/components/table/table.css @@ -55,10 +55,19 @@ input { background-color: var(--palegreen); } +.selected { + font-weight: bold; +} + .btn-confirm { display: none; } .confirm-btn:hover { background-color: var(--green); +} + +.hover-effect:hover { + cursor: pointer; + background-color: var(--verypalegreen); /* You can choose any color you like */ } \ No newline at end of file diff --git a/js/views/02_baseline_landing_page/helpers.js b/js/views/02_baseline_landing_page/helpers.js new file mode 100644 index 0000000..03d0da5 --- /dev/null +++ b/js/views/02_baseline_landing_page/helpers.js @@ -0,0 +1,58 @@ + +import Subtitle from '../../components/header/header.js' +import Prompt from '../../components/prompt/prompt.js' +import NavButtons from '../../components/nav_buttons/nav_buttons.js' +import Table from "../../components/table/table.js"; +import { DATA_ROOT } from "../../init.js"; +import Body from "../../components/body/body.js"; + +const fundCols = [ + { title: 'ID', className: 'fund-id' }, + { title: 'Name', className: 'fund-name' }, +]; + +export function preparePageView(){ + + localStorage.setItem("fund", ''); + + // prepare page view + Body.clearAll(); + NavButtons.show(); + + // update page text + Subtitle.update('Baseline Budget Request'); + // TODO: update to make dynamic + Prompt.Text.update(`We will now ask you a series of questions about your BASELINE budget request. + At the end, we will ask you about any new initiatives (ie. supplemental requests). + Select one of your funds to begin.`); +} + +function allowRowSelection(){ + var tableRows = document.querySelectorAll("tbody tr"); + tableRows.forEach(function(row) { + row.addEventListener('mouseover', function() { + this.classList.add('hover-effect'); + }); + row.addEventListener('mouseout', function() { + this.classList.remove('hover-effect'); + }); + row.addEventListener('click', function() { + tableRows.forEach(function(tableRow) { + tableRow.classList = ''; + }); + this.classList.add('selected'); + // get fund + var fund = this.querySelector('.fund-name').textContent; + // save selected fund + localStorage.setItem("fund", fund); + }); + }); +} + +export async function initializeFundTable(){ + await Table.Data.loadFromJSON(DATA_ROOT + 'funds.json') + Table.adjustWidth('100%'); + Table.show(); + Table.Columns.assignClasses(fundCols); + allowRowSelection(); +} \ No newline at end of file diff --git a/js/views/02_baseline_landing_page/main.js b/js/views/02_baseline_landing_page/main.js index 7a40b3a..6061c56 100644 --- a/js/views/02_baseline_landing_page/main.js +++ b/js/views/02_baseline_landing_page/main.js @@ -1,27 +1,10 @@ import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; -import Subtitle from '../../components/header/header.js' -import Prompt from '../../components/prompt/prompt.js' -import NavButtons from '../../components/nav_buttons/nav_buttons.js' -import Table from "../../components/table/table.js"; -import { DATA_ROOT } from "../../init.js"; -import Body from "../../components/body/body.js"; +import { preparePageView, initializeFundTable } from "../02_baseline_landing_page/helpers.js"; + export function loadBaselineLandingPage(){ //update page state updatePageState('baseline-landing'); - - // prepare page view - Body.clearAll(); - NavButtons.show(); - - // update page text - Subtitle.update('Baseline Budget Request'); - // TODO: update to make dynamic - Prompt.Text.update(`We will now ask you a series of questions about your BASELINE budget request. - At the end, we will ask you about any new initiatives (ie. supplemental requests). - Select one of your funds to begin.`); - - Table.Data.loadFromJSON(DATA_ROOT + 'funds.json') - Table.adjustWidth('100%'); - Table.show(); -} \ No newline at end of file + preparePageView(); + initializeFundTable(); +} diff --git a/js/views/04.5_OT/main.js b/js/views/04.5_OT/main.js index 1181a30..8c9be85 100644 --- a/js/views/04.5_OT/main.js +++ b/js/views/04.5_OT/main.js @@ -3,6 +3,7 @@ import { updatePageState } from "../../utils/data_utils/local_storage_handlers.j import Body from '../../components/body/body.js'; import NavButtons from '../../components/nav_buttons/nav_buttons.js'; import Subtitle from '../../components/header/header.js'; +import Sidebar from '../../components/sidebar/sidebar.js'; export function loadOTPage(){ //update page state @@ -11,6 +12,7 @@ export function loadOTPage(){ // prepare page view Body.clearAll(); NavButtons.show(); + Sidebar.show(); // update page text Subtitle.update('Overtime Estimates'); From 905f1e13725bfa3009b025071a44d18e0bc7dd0b Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Tue, 2 Jul 2024 11:00:28 -0400 Subject: [PATCH 21/30] updated submission forms; editted nonpersonnel table --- data/law_dept_sample/nonpersonnel_data.json | 12 +++-- js/components/form/form.js | 3 +- js/components/table/subcomponents/rows.js | 2 + js/views/04_personnel/helpers.js | 54 +++++++++++++++++++++ js/views/04_personnel/main.js | 4 +- js/views/05_nonpersonnel/helpers.js | 1 + js/views/06_new_initiatives/helpers.js | 3 +- 7 files changed, 72 insertions(+), 7 deletions(-) diff --git a/data/law_dept_sample/nonpersonnel_data.json b/data/law_dept_sample/nonpersonnel_data.json index dd844da..3402869 100644 --- a/data/law_dept_sample/nonpersonnel_data.json +++ b/data/law_dept_sample/nonpersonnel_data.json @@ -2,19 +2,25 @@ { "Vendor": "Law Firm LLC", "Account String": "1000-29320-320010", - "Object": 613100, + "Object": "Consulting", + "End of Contract": "12/31/2024", + "Amount Remaining" : 50000, "Request Total": 100000 }, { "Vendor": "Office Supplier", "Account String": "1000-29320-320010", - "Object": 613100, + "Object": "Office Supplies", + "End of Contract": "12/31/2026", + "Amount Remaining" : 500000, "Request Total": 10000 }, { "Vendor": "Software Co.", "Account String": "1000-29320-320010", - "Object": 613100, + "Object": "Information Technology", + "End of Contract": "6/1/2025", + "Amount Remaining" : 30000, "Request Total": 30000 } ] \ No newline at end of file diff --git a/js/components/form/form.js b/js/components/form/form.js index 4cf9361..083a20a 100644 --- a/js/components/form/form.js +++ b/js/components/form/form.js @@ -3,7 +3,6 @@ import NewField from "./subcomponents/fields.js"; import SubmitButton from "./subcomponents/submit.js"; function fetchAllResponses(event) { - event.preventDefault(); // Prevent the default form submission // Assuming `event.target` is the form itself const form = event.target; @@ -45,7 +44,7 @@ function addForm(element_id = 'modal-body', form_id = 'new-form') { export const Form = { new : function(parent_elem_id) { addForm(parent_elem_id, 'new-form') }, - fetchAllResponses : function(event) { fetchAllResponses(event) }, + fetchAllResponses : function(event) { return fetchAllResponses(event) }, NewField : NewField, Dropdown : Dropdown, SubmitButton : SubmitButton diff --git a/js/components/table/subcomponents/rows.js b/js/components/table/subcomponents/rows.js index 0668df8..0c5b6b7 100644 --- a/js/components/table/subcomponents/rows.js +++ b/js/components/table/subcomponents/rows.js @@ -21,6 +21,8 @@ function addNewRow(data_dictionary){ new_row.appendChild(newCell); } + console.log(new_row); + // Append the new row to the table body let tbody = table.querySelector('tbody'); tbody.appendChild(new_row); diff --git a/js/views/04_personnel/helpers.js b/js/views/04_personnel/helpers.js index 5482781..b6a6b58 100644 --- a/js/views/04_personnel/helpers.js +++ b/js/views/04_personnel/helpers.js @@ -5,6 +5,8 @@ import { DATA_ROOT, fringe, cola, merit } from "../../init.js" import Body from "../../components/body/body.js"; import NavButtons from "../../components/nav_buttons/nav_buttons.js"; import Subtitle from "../../components/header/header.js"; +import Form from "../../components/form/form.js"; +import Modal from "../../components/modal/modal.js"; const personnelColumns = [ { title: 'Job Name', className: 'job-name' }, @@ -47,6 +49,12 @@ export async function initializePersonnelTable(){ Table.Columns.assignClasses(personnelColumns); // activate edit buttons Table.Buttons.Edit.init(personnelRowOnEdit, updateDisplayandTotals); + initializeRowAddition(); +} + +function initializeRowAddition(){ + Table.Buttons.AddRow.updateText("Add new job name"); + Table.Buttons.AddRow.show(); } function calculateTotalCost(ftes, avg_salary, fringe, cola, merit){ @@ -79,3 +87,49 @@ function updateDisplayandTotals(){ Table.Cell.updateValue(rows[i], 'total-supp', total_supp_cost); } } + + +export function setUpModal() { + // Initialize modal + Modal.clear(); + Modal.Link.add('add-btn'); + Modal.Title.update('New job'); +} + +export function setUpForm() { + // Set up form + Form.new('modal-body'); + Form.NewField.shortText('Job Name:', 'job-name', true); + Form.NewField.shortText('Account String:', 'account-string', true); + // Form.NewField.longText('Explain why this initiative is necessary and describe its potential impact.', 'Explanation', true); + // Form.NewField.numericInput('Estimate of ADDITONAL personnel cost?', 'Personnel Cost', true); + // Form.NewField.numericInput('Estimate of ADDITONAL nonpersonnel cost?', 'Non-personnel Cost', true); + // Form.NewField.numericInput('Estimate of TOTAL ADDITIONAL cost?', 'Total Cost', true); + Form.SubmitButton.add(); + // Initialize form submission to table data + handleFormSubmissions(); +} + +function handleFormSubmissions(event){ + // initialize form submission + + const modal = document.getElementById('main-modal'); + modal.addEventListener('submit', function(event) { + event.preventDefault(); + // 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(); + + // add data to table + Table.Rows.add(responses); + Table.show(); + Table.Buttons.AddRow.show(); + // TODO: save table data + // TODO: edit cost to show currency correctly + } + + }) +} diff --git a/js/views/04_personnel/main.js b/js/views/04_personnel/main.js index 581a93d..8e39d92 100644 --- a/js/views/04_personnel/main.js +++ b/js/views/04_personnel/main.js @@ -1,6 +1,6 @@ import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; -import { preparePageView, initializePersonnelTable } from "./helpers.js"; +import { preparePageView, initializePersonnelTable, setUpModal, setUpForm } from "./helpers.js"; export function loadPersonnelPage(){ @@ -8,5 +8,7 @@ export function loadPersonnelPage(){ preparePageView(); initializePersonnelTable(); + setUpModal(); + setUpForm(); } diff --git a/js/views/05_nonpersonnel/helpers.js b/js/views/05_nonpersonnel/helpers.js index 7793b3c..aa63976 100644 --- a/js/views/05_nonpersonnel/helpers.js +++ b/js/views/05_nonpersonnel/helpers.js @@ -8,6 +8,7 @@ import Subtitle from "../../components/header/header.js"; const nonPersonnelColumns = [ { title: 'Request Total', className: 'request', isCost: true }, + { title: 'Amount Remaining', className: 'remaining', isCost: true }, ]; export function preparePageView(){ diff --git a/js/views/06_new_initiatives/helpers.js b/js/views/06_new_initiatives/helpers.js index eed514a..246b29e 100644 --- a/js/views/06_new_initiatives/helpers.js +++ b/js/views/06_new_initiatives/helpers.js @@ -53,8 +53,9 @@ export function handleFormSubmissions(event){ // initialize form submission const modal = document.getElementById('main-modal'); modal.addEventListener('submit', function(event) { + event.preventDefault(); // get answers from form, hide form, show answers in table - const responses = fetchAllResponses(event); + const responses = Form.fetchAllResponses(event); // make sure it's not an empty response if (Object.values(responses)[0] != ''){ // change page view From 519a8d01f05dceb45f5fb2cf01d7a368dfdb7047 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Tue, 2 Jul 2024 16:39:31 -0400 Subject: [PATCH 22/30] disabled next buttons where relevant; storing fund info; move revenue page up; edit prompt buttons; added revenue page cleanup function --- data/law_dept_sample/nonpersonnel_data.json | 3 ++ js/components/body/body.js | 8 ++-- js/components/header/header.css | 2 +- js/components/nav_buttons/nav_buttons.css | 11 ++++- js/components/nav_buttons/nav_buttons.js | 21 ++++++++++ js/components/prompt/prompt.css | 20 +++++++-- js/components/prompt/subcomponents/buttons.js | 41 ++++++++++++++----- js/views/00_welcome/helpers.js | 2 +- js/views/01_upload/helpers.js | 14 +++++-- js/views/01_upload/main.js | 2 +- js/views/02_baseline_landing_page/helpers.js | 26 ++++++++---- js/views/03_revenue/helpers.js | 0 js/views/03_revenue/main.js | 20 ++++++--- js/views/04.5_OT/main.js | 2 +- js/views/04_personnel/helpers.js | 2 +- js/views/05_nonpersonnel/helpers.js | 2 +- js/views/06_new_initiatives/helpers.js | 4 +- js/views/07_summary/main.js | 2 +- js/views/view_logic.js | 5 ++- 19 files changed, 141 insertions(+), 46 deletions(-) create mode 100644 js/views/03_revenue/helpers.js diff --git a/data/law_dept_sample/nonpersonnel_data.json b/data/law_dept_sample/nonpersonnel_data.json index 3402869..0f93cce 100644 --- a/data/law_dept_sample/nonpersonnel_data.json +++ b/data/law_dept_sample/nonpersonnel_data.json @@ -1,6 +1,7 @@ [ { "Vendor": "Law Firm LLC", + "CPA #" : "765421", "Account String": "1000-29320-320010", "Object": "Consulting", "End of Contract": "12/31/2024", @@ -9,6 +10,7 @@ }, { "Vendor": "Office Supplier", + "CPA #" : "1234567", "Account String": "1000-29320-320010", "Object": "Office Supplies", "End of Contract": "12/31/2026", @@ -17,6 +19,7 @@ }, { "Vendor": "Software Co.", + "CPA #" : "9876543", "Account String": "1000-29320-320010", "Object": "Information Technology", "End of Contract": "6/1/2025", diff --git a/js/components/body/body.js b/js/components/body/body.js index b001ca1..5f1eced 100644 --- a/js/components/body/body.js +++ b/js/components/body/body.js @@ -5,8 +5,7 @@ import Prompt from '../prompt/prompt.js'; import Sidebar from '../sidebar/sidebar.js'; import Table from '../table/table.js'; - -function clearAll() { +function resetPage() { // hide everything in the body Welcome.hide(); Modal.clear(); @@ -15,10 +14,13 @@ function clearAll() { Prompt.hide(); Table.hide(); Sidebar.hide(); + // disable next button + NavButtons.Next.disable(); + Prompt.Buttons.reset(); } export const Body = { - clearAll : clearAll + reset : resetPage } export default Body; \ No newline at end of file diff --git a/js/components/header/header.css b/js/components/header/header.css index ccd9d76..19ca544 100644 --- a/js/components/header/header.css +++ b/js/components/header/header.css @@ -4,6 +4,6 @@ h1 { } h2 { - color: var(--citygreen); + color: var(--darkGray); text-align: center; } \ No newline at end of file diff --git a/js/components/nav_buttons/nav_buttons.css b/js/components/nav_buttons/nav_buttons.css index 8560007..8d27b61 100644 --- a/js/components/nav_buttons/nav_buttons.css +++ b/js/components/nav_buttons/nav_buttons.css @@ -4,9 +4,18 @@ } #btn-next, #btn-last { - background-color: var(--blue); + background-color: var(--darkGray); } #btn-next:hover, #btn-last:hover { background-color: var(--yellow); + color: var(--darkGray); +} + +/* Add style for when you cannot click the next button */ +#btn-next.disabled, #btn-last.disabled, +#btn-next.disabled:hover, #btn-last.disabled:hover { + background-color: gray; + color: white; + pointer-events: none; } \ No newline at end of file diff --git a/js/components/nav_buttons/nav_buttons.js b/js/components/nav_buttons/nav_buttons.js index fadc302..1be8de5 100644 --- a/js/components/nav_buttons/nav_buttons.js +++ b/js/components/nav_buttons/nav_buttons.js @@ -7,6 +7,7 @@ function initializeNavButtons(){ // initialize next button const next_btn = document.getElementById('btn-next'); next_btn.addEventListener('click', nextPage); + disable('btn-next'); } function hideNavButtons() { @@ -18,9 +19,29 @@ function showNavButtons() { initializeNavButtons(); } +function disable(button_id) { + document.getElementById(button_id).classList.add('disabled'); +} + +function enable(button_id) { + document.getElementById(button_id).classList.remove('disabled'); +} + +const Next = { + disable : function() { disable('btn-next') }, + enable : function() { enable('btn-next') } +} + +const Last = { + disable : function() { disable('btn-last') }, + enable : function() { enable('btn-last') } +} + export const NavButtons = { hide : hideNavButtons, show : showNavButtons, + Next : Next, + Last : Last } export default NavButtons; \ No newline at end of file diff --git a/js/components/prompt/prompt.css b/js/components/prompt/prompt.css index 968f9eb..ac7867e 100644 --- a/js/components/prompt/prompt.css +++ b/js/components/prompt/prompt.css @@ -9,6 +9,20 @@ text-align: center; } -#option1 { background-color: var(--green);} -#option2 {background-color: var(--orange);} -#option1, #option2 { font-size: 1.5em; } \ No newline at end of file +#option1, #option2 { + font-size: 1.5em; + border-color: var(--citygreen); + border-width: 2px; + background-color: var(--white); + color: var(--citygreen); +} + +#option1:hover, #option2:hover { + background-color: var(--palegreen); + color: var(--citygreen); +} + +#option2.clicked, #option1.clicked { + font-weight: bold; + background-color: var(--palegreen); +} diff --git a/js/components/prompt/subcomponents/buttons.js b/js/components/prompt/subcomponents/buttons.js index 076c0f8..88b564a 100644 --- a/js/components/prompt/subcomponents/buttons.js +++ b/js/components/prompt/subcomponents/buttons.js @@ -8,26 +8,46 @@ function updatePromptButton(id, text){ showPromptButton(id); } +function hidePromptButton(id){ + document.getElementById(id).style.display = 'none'; +} + +function unclickAll(){ + document.getElementById('option1').classList.remove('clicked'); + document.getElementById('option2').classList.remove('clicked'); +} + +function applyClickedStyle(button){ + unclickAll(); + button.classList.add('clicked'); +} + function addPromptButtonAction(button_id, action_fn){ - document.getElementById(button_id).addEventListener('click', action_fn); + const buttonElement = document.getElementById(button_id); + buttonElement.addEventListener('click', action_fn); + buttonElement.addEventListener('click', function(){ + applyClickedStyle(this); + }); } -function hidePromptButton(id){ - document.getElementById(id).style.display = 'none'; +function removePromptButtonAction(button_id, action_fn){ + document.getElementById(button_id).removeEventListener('click', action_fn); } export const Left = { - show : showPromptButton('option1'), - hide : hidePromptButton('option1'), + show : function() { showPromptButton('option1') }, + hide : function() { hidePromptButton('option1') }, updateText : function(text) { updatePromptButton('option1', text) }, - addAction : function(action_fn) { addPromptButtonAction('option1', action_fn) } + addAction : function(action_fn) { addPromptButtonAction('option1', action_fn) }, + removeAction : function(action_fn) { removePromptButtonAction('option1', action_fn) } } export const Right = { - show : showPromptButton('option2'), - hide : hidePromptButton('option2'), + show : function() { showPromptButton('option2') }, + hide : function() { hidePromptButton('option2') }, updateText : function(text) { updatePromptButton('option2', text) }, - addAction : function(action_fn) { addPromptButtonAction('option2', action_fn) } + addAction : function(action_fn) { addPromptButtonAction('option2', action_fn) }, + removeAction : function(action_fn) { removePromptButtonAction('option2', action_fn) } } export const Buttons = { @@ -40,7 +60,8 @@ export const Buttons = { hide : function() { hidePromptButton('option1'); hidePromptButton('option2'); - } + }, + reset : unclickAll } export default Buttons; \ No newline at end of file diff --git a/js/views/00_welcome/helpers.js b/js/views/00_welcome/helpers.js index 539fa4d..3053bb8 100644 --- a/js/views/00_welcome/helpers.js +++ b/js/views/00_welcome/helpers.js @@ -9,7 +9,7 @@ import { loadUploadPage } from '../01_upload/main.js' export function initializePageView(){ // page set up - Body.clearAll(); + Body.reset(); Subtitle.update("Welcome"); Welcome.show(); } diff --git a/js/views/01_upload/helpers.js b/js/views/01_upload/helpers.js index c8a2587..c908f65 100644 --- a/js/views/01_upload/helpers.js +++ b/js/views/01_upload/helpers.js @@ -3,15 +3,21 @@ import Prompt from '../../components/prompt/prompt.js' import NavButtons from '../../components/nav_buttons/nav_buttons.js' import Body from "../../components/body/body.js"; -function initializePageView() { +export function initializePageView() { // prepare page view - Body.clearAll(); + Body.reset(); NavButtons.show(); // update page text Subtitle.update('Excel Upload'); - // TODO: update to make dynamic + // TODO: update to make upload actually work Prompt.Text.update(`Placeholder for Excel Upload`); - ///Prompt.Buttons.Right.addAction(initializeWelcomePage) + Prompt.Buttons.Left.updateText('Upload'); + Prompt.Buttons.Left.show(); + Prompt.Buttons.Left.addAction(uploadExcelAction); } + +function uploadExcelAction() { + NavButtons.Next.enable(); +} \ No newline at end of file diff --git a/js/views/01_upload/main.js b/js/views/01_upload/main.js index a082d44..cd4e55a 100644 --- a/js/views/01_upload/main.js +++ b/js/views/01_upload/main.js @@ -1,5 +1,5 @@ import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; -import { initializePageView } from "../06_new_initiatives/helpers.js"; +import { initializePageView } from "./helpers.js"; export function loadUploadPage(){ //update page state diff --git a/js/views/02_baseline_landing_page/helpers.js b/js/views/02_baseline_landing_page/helpers.js index 03d0da5..191af54 100644 --- a/js/views/02_baseline_landing_page/helpers.js +++ b/js/views/02_baseline_landing_page/helpers.js @@ -16,7 +16,7 @@ export function preparePageView(){ localStorage.setItem("fund", ''); // prepare page view - Body.clearAll(); + Body.reset(); NavButtons.show(); // update page text @@ -37,14 +37,7 @@ function allowRowSelection(){ this.classList.remove('hover-effect'); }); row.addEventListener('click', function() { - tableRows.forEach(function(tableRow) { - tableRow.classList = ''; - }); - this.classList.add('selected'); - // get fund - var fund = this.querySelector('.fund-name').textContent; - // save selected fund - localStorage.setItem("fund", fund); + selectFund(tableRows, this); }); }); } @@ -55,4 +48,19 @@ export async function initializeFundTable(){ Table.show(); Table.Columns.assignClasses(fundCols); allowRowSelection(); +} + +function selectFund(tableRows, selected_row){ + // remove selected class from any other rows + tableRows.forEach(function(tableRow) { + tableRow.classList = ''; + }); + // add selected class to clicked row + selected_row.classList.add('selected'); + // get fund and save selected fund + var fund = selected_row.querySelector('.fund-name').textContent; + localStorage.setItem("fund", fund); + + // enable next step + 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 new file mode 100644 index 0000000..e69de29 diff --git a/js/views/03_revenue/main.js b/js/views/03_revenue/main.js index e5be62a..59060fe 100644 --- a/js/views/03_revenue/main.js +++ b/js/views/03_revenue/main.js @@ -11,21 +11,31 @@ export function loadRevenuePage() { //update page state updatePageState('revenue'); + localStorage.setItem("fund", ''); // prepare page view - Body.clearAll(); + Body.reset(); NavButtons.show(); // update page text Subtitle.update('Revenue Projections'); // TODO: update to make dynamic Prompt.Text.update(`Your revenue projection for FY26 is ${formatCurrency(REVENUE, true)}`); - Prompt.Buttons.Left.updateText('Confirm and continue.'); + Prompt.Buttons.Left.updateText('Confirm'); Prompt.Buttons.Right.updateText("This doesn't look right"); - // clicking 'confirm and continue' will also take us to the next page + // clicking 'confirm' will also take us to the next page Prompt.Buttons.Left.addAction(nextPage); // TODO: allow user to edit revenue here - // addPromptButtonAction('option2', nextPage); + Prompt.Buttons.Right.addAction(handleRevenueEdit); +} -} \ No newline at end of file +function handleRevenueEdit() { + NavButtons.Next.enable(); +} + +export function cleanupRevenuePage() { + // remove event listeners on prompt buttons + Prompt.Buttons.Left.removeAction(nextPage); + Prompt.Buttons.Right.removeAction(handleRevenueEdit); +}; \ No newline at end of file diff --git a/js/views/04.5_OT/main.js b/js/views/04.5_OT/main.js index 8c9be85..916f7f3 100644 --- a/js/views/04.5_OT/main.js +++ b/js/views/04.5_OT/main.js @@ -10,7 +10,7 @@ export function loadOTPage(){ updatePageState('overtime'); // prepare page view - Body.clearAll(); + Body.reset(); NavButtons.show(); Sidebar.show(); diff --git a/js/views/04_personnel/helpers.js b/js/views/04_personnel/helpers.js index b6a6b58..055e61b 100644 --- a/js/views/04_personnel/helpers.js +++ b/js/views/04_personnel/helpers.js @@ -20,7 +20,7 @@ const personnelColumns = [ export function preparePageView(){ // prepare page view - Body.clearAll(); + Body.reset(); NavButtons.show(); Sidebar.show(); Table.adjustWidth('90%'); diff --git a/js/views/05_nonpersonnel/helpers.js b/js/views/05_nonpersonnel/helpers.js index aa63976..c48e5ac 100644 --- a/js/views/05_nonpersonnel/helpers.js +++ b/js/views/05_nonpersonnel/helpers.js @@ -13,7 +13,7 @@ const nonPersonnelColumns = [ export function preparePageView(){ // prepare page view - Body.clearAll(); + Body.reset(); NavButtons.show(); Sidebar.show(); Table.adjustWidth('100%'); diff --git a/js/views/06_new_initiatives/helpers.js b/js/views/06_new_initiatives/helpers.js index 246b29e..ef07fe2 100644 --- a/js/views/06_new_initiatives/helpers.js +++ b/js/views/06_new_initiatives/helpers.js @@ -10,7 +10,7 @@ import Subtitle from '../../components/header/header.js' export function initializePageView() { // Prepare page view - Body.clearAll(); + Body.reset(); NavButtons.show(); Prompt.Buttons.Right.addAction(nextPage); @@ -34,9 +34,9 @@ export function setUpForm() { Form.new('modal-body'); Form.NewField.shortText('Initiative Name:', 'Initiative Name', true); Form.NewField.longText('Explain why this initiative is necessary and describe its potential impact.', 'Explanation', true); + Form.NewField.numericInput('Estimate of ADDITONAL revenue associated with this initiative?', 'Revenue', true); Form.NewField.numericInput('Estimate of ADDITONAL personnel cost?', 'Personnel Cost', true); Form.NewField.numericInput('Estimate of ADDITONAL nonpersonnel cost?', 'Non-personnel Cost', true); - Form.NewField.numericInput('Estimate of TOTAL ADDITIONAL cost?', 'Total Cost', true); Form.SubmitButton.add(); // Initialize form submission to table data handleFormSubmissions(); diff --git a/js/views/07_summary/main.js b/js/views/07_summary/main.js index 97ac09b..8de4dd7 100644 --- a/js/views/07_summary/main.js +++ b/js/views/07_summary/main.js @@ -9,7 +9,7 @@ export function loadSummaryPage(){ updatePageState('summary'); // prepare page view - Body.clearAll(); + Body.reset(); Prompt.Buttons.Left.updateText('Submit'); Prompt.Buttons.Right.updateText('Go back and edit'); diff --git a/js/views/view_logic.js b/js/views/view_logic.js index 8b4dfa8..3d0847d 100644 --- a/js/views/view_logic.js +++ b/js/views/view_logic.js @@ -1,6 +1,6 @@ import { initializeWelcomePage } from './00_welcome/main.js'; import { cleanUpInitiativesPage, loadNewInitiatives } from './06_new_initiatives/main.js' -import { loadRevenuePage } from './03_revenue/main.js' +import { loadRevenuePage, cleanupRevenuePage } from './03_revenue/main.js' import { loadPersonnelPage } from './04_personnel/main.js'; import { loadOTPage } from './04.5_OT/main.js'; import { loadNonpersonnelPage } from './05_nonpersonnel/main.js'; @@ -13,8 +13,8 @@ import { loadPageState } from '../utils/data_utils/local_storage_handlers.js'; export let PAGES = { 'welcome' : initializeWelcomePage, 'upload' : loadUploadPage, - 'baseline-landing' : loadBaselineLandingPage, 'revenue' : loadRevenuePage, + 'baseline-landing' : loadBaselineLandingPage, 'personnel' : loadPersonnelPage, 'overtime' : loadOTPage, 'nonpersonnel' : loadNonpersonnelPage, @@ -24,6 +24,7 @@ export let PAGES = { export let CLEANUP = { 'new-inits' : cleanUpInitiativesPage, + 'revenue' : cleanupRevenuePage } export function nextPage(){ From b4a9b3f54174cf364bfde2eb953130aa30415e9d Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Wed, 3 Jul 2024 09:59:23 -0400 Subject: [PATCH 23/30] fixed page flow with nav buttons and added pauses --- js/utils/common_utils.js | 7 +++++++ js/views/03_revenue/main.js | 8 ++++---- js/views/04.5_OT/main.js | 4 ++++ js/views/04_personnel/helpers.js | 3 +++ js/views/05_nonpersonnel/helpers.js | 4 ++++ js/views/06_new_initiatives/helpers.js | 15 +++++++++++++-- js/views/06_new_initiatives/main.js | 4 +++- js/views/view_logic.js | 6 ++++++ 8 files changed, 44 insertions(+), 7 deletions(-) diff --git a/js/utils/common_utils.js b/js/utils/common_utils.js index 082f22a..afaf60b 100644 --- a/js/utils/common_utils.js +++ b/js/utils/common_utils.js @@ -22,3 +22,10 @@ const unformatCurrency = (formattedAmount) => { return parseFloat(numericalPart); }; +function delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export async function pauseExecution(seconds) { + await delay(seconds * 1000); // convert to milliseconds +} \ No newline at end of file diff --git a/js/views/03_revenue/main.js b/js/views/03_revenue/main.js index 59060fe..a7bf097 100644 --- a/js/views/03_revenue/main.js +++ b/js/views/03_revenue/main.js @@ -4,7 +4,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 { nextPage } from '../view_logic.js' +import { pauseAndContinue } from '../view_logic.js' import Subtitle from '../../components/header/header.js' export function loadRevenuePage() { @@ -25,7 +25,7 @@ export function loadRevenuePage() { Prompt.Buttons.Right.updateText("This doesn't look right"); // clicking 'confirm' will also take us to the next page - Prompt.Buttons.Left.addAction(nextPage); + Prompt.Buttons.Left.addAction(pauseAndContinue); // TODO: allow user to edit revenue here Prompt.Buttons.Right.addAction(handleRevenueEdit); } @@ -36,6 +36,6 @@ function handleRevenueEdit() { export function cleanupRevenuePage() { // remove event listeners on prompt buttons - Prompt.Buttons.Left.removeAction(nextPage); - Prompt.Buttons.Right.removeAction(handleRevenueEdit); + Prompt.Buttons.Left.removeAction(pauseAndContinue); + Prompt.Buttons.Right.removeAction(); }; \ No newline at end of file diff --git a/js/views/04.5_OT/main.js b/js/views/04.5_OT/main.js index 916f7f3..e84def6 100644 --- a/js/views/04.5_OT/main.js +++ b/js/views/04.5_OT/main.js @@ -14,6 +14,10 @@ export function loadOTPage(){ NavButtons.show(); Sidebar.show(); + // just enable next for now + // TODO: only enable when all info is entered + NavButtons.Next.enable(); + // update page text Subtitle.update('Overtime Estimates'); // TODO: update to make dynamic diff --git a/js/views/04_personnel/helpers.js b/js/views/04_personnel/helpers.js index 055e61b..261a61b 100644 --- a/js/views/04_personnel/helpers.js +++ b/js/views/04_personnel/helpers.js @@ -24,6 +24,9 @@ export function preparePageView(){ NavButtons.show(); Sidebar.show(); Table.adjustWidth('90%'); + // just enable next for now + // TODO only enable when all info is entered + NavButtons.Next.enable(); // update page text Subtitle.update('Personnel'); diff --git a/js/views/05_nonpersonnel/helpers.js b/js/views/05_nonpersonnel/helpers.js index c48e5ac..7503186 100644 --- a/js/views/05_nonpersonnel/helpers.js +++ b/js/views/05_nonpersonnel/helpers.js @@ -21,6 +21,10 @@ export function preparePageView(){ Subtitle.update('Non-Personnel'); Prompt.Text.update('Select an action item for each non-personnel line item from last year.'); updateDisplayandTotals(); + + // just enable next for now + // TODO: only enable when all info is entered + NavButtons.Next.enable(); } export async function initializeNonpersonnelTable(){ diff --git a/js/views/06_new_initiatives/helpers.js b/js/views/06_new_initiatives/helpers.js index ef07fe2..0add13e 100644 --- a/js/views/06_new_initiatives/helpers.js +++ b/js/views/06_new_initiatives/helpers.js @@ -5,20 +5,26 @@ 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 { nextPage } from '../view_logic.js' +import { pauseAndContinue } from '../view_logic.js' import Subtitle from '../../components/header/header.js' export function initializePageView() { // Prepare page view Body.reset(); NavButtons.show(); - Prompt.Buttons.Right.addAction(nextPage); // Load text Subtitle.update('New Initiatives'); Prompt.Text.update('Do you have any new initiatives for FY26?'); 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.Left.addAction(enableContinue); +} + +function enableContinue() { + NavButtons.Next.enable(); } export function setUpModal() { @@ -76,4 +82,9 @@ export function handleFormSubmissions(event){ export function removeModalLinks(){ Modal.Link.remove('option1'); Modal.Link.remove('add-btn'); +} + +export function removePromptButtonListeners(){ + Prompt.Buttons.Right.removeAction(pauseAndContinue); + Prompt.Buttons.Left.removeAction(enableContinue); } \ 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 94e84b0..78039fb 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 } from './helpers.js' +import { initializePageView, setUpModal, setUpForm, setUpTable, removeModalLinks, removePromptButtonListeners } from './helpers.js' import { updatePageState } from '../../utils/data_utils/local_storage_handlers.js' @@ -14,4 +14,6 @@ export function loadNewInitiatives() { export function cleanUpInitiativesPage() { removeModalLinks(); + // remove event listeners on prompt buttons + removePromptButtonListeners(); } \ No newline at end of file diff --git a/js/views/view_logic.js b/js/views/view_logic.js index 3d0847d..5ffdfd2 100644 --- a/js/views/view_logic.js +++ b/js/views/view_logic.js @@ -7,6 +7,7 @@ import { loadNonpersonnelPage } from './05_nonpersonnel/main.js'; import { loadBaselineLandingPage } from './02_baseline_landing_page/main.js'; import { loadSummaryPage } from './07_summary/main.js'; import { loadUploadPage } from './01_upload/main.js'; +import { pauseExecution } from '../utils/common_utils.js'; import { loadPageState } from '../utils/data_utils/local_storage_handlers.js'; @@ -65,4 +66,9 @@ export function lastPage(){ const lastFn = PAGES[lastKey]; lastFn(); } +} + +export async function pauseAndContinue(){ + await pauseExecution(0.5); + nextPage(); } \ No newline at end of file From bba65c91930d210c7476e7186340f3b2a5d0702a Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Wed, 3 Jul 2024 16:11:38 -0400 Subject: [PATCH 24/30] changed style; fixed cleanup bug from summary view --- assets/city-of-detroit-logo.png | Bin 0 -> 5589 bytes css/common.css | 8 +++- data/law_dept_sample/personnel_data.json | 18 +++----- index.html | 2 + js/components/body/body.css | 1 + js/components/prompt/prompt.css | 8 ++-- js/components/table/table.css | 1 + js/components/welcome/welcome.css | 16 +++---- js/init.js | 12 ++--- js/views/04_personnel/helpers.js | 55 ++++++++++------------- js/views/04_personnel/main.js | 1 - js/views/06_new_initiatives/helpers.js | 8 +--- js/views/07_summary/main.js | 14 ++++-- js/views/view_logic.js | 27 ++++++++--- 14 files changed, 92 insertions(+), 79 deletions(-) create mode 100644 assets/city-of-detroit-logo.png diff --git a/assets/city-of-detroit-logo.png b/assets/city-of-detroit-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..856d278282f5542da4615ed5a6de510a65aad4bb GIT binary patch literal 5589 zcmaJ_cQl;a*B-$rA!>9n8A6CU`WR&xgE2%WqK}CYWd=r01ar004}7x(JiA zG3D%&paq`&E8fRToDJMWZ7ZTF{ua^C(HjeZJL8?OAUzL97pw`^(K*2D9To-vP>Q;m zSrM%a&`=ECL(=iLkEFi`;S3D`z|{Q-juq)8aQHHJ%3w7|AZJAK=nL*h>o5ZtR6xYd`2Sa>go)Y zQP$ShQcy%_YiTRV$Y>*!rKJ^-3R<#q5G5I;qKwktSOgy9>w)zo{*871FZPV!U$DPB z!h>)Y8G-e7y@Pc|dgDDne`XDJ{dX;z|H}6l*7@JHX#Fcz>P(E(@7n&aTK{c2YoOoL zf4cW<@lXF_JeZFHc^}a0IQ}R0&eF2W!2`qzuDL9(8W+a;cWf&%YM?VEfX3~ z!azdlqxamXiMC^-4ajm&OCqP2`N5N4DSPhYwjATB&&+PtvU-^#(C3`s@eW`(Ehk;Q zNE)4Bl{P}uq)>o^=T0p3BFGBP<4uzfVMu)N)r?TLsQbFL6dkr z>X7%)^soPDiUPyrgzj#DoKCtIV~YwdLhkcG%6a&<^uo7_jTTJNY&*B?y7j8EG<8zY zo8eE$Lic(#wYDdve$vsfR2dDP2!?efEV)%gfo;x{il(JfQ028ca-F->TStmc&s16K z#dX9orG5l8#o}@^$>Q@zHs?Gr!*|5w5O)PYJq1dwlwlXC_vfS~0>#8;amyM%S1u+$ zH5YHJ85tG39E$LgC?58sv!Q_ro` zwU14E;C-#+^1{wT(TW0G{-^7kB760bu;jVI88zeB$F;6;*Q@}RewDAj)rro1Fg~ay zS8M$tpnL(VxjkR&nGLmRw94fp{pd4;`du}Ccmy)(o?ZK{bJsNJ{diawWoYFFN9yx6 zLO?eq3&m4&*Z0pxx|w9`P2=+OEUvYt=&<$+>Cpyf3k4;7*fbVC%r0FFWC~5y^X21% zjc^(KN?*ztv?QPz_>YYcE1H6G%WwpG=DR@H;I~&wPrD(KNjHo7vw+m^GQ`1rNk07q z#cWYM(rOHVe7>*57|v0eQrMe=(n0ECs}bh41yiBVX7@8JcRGx0%dQx|O?%Y)%k-({ zW>S>Z^=Bqj1B!unIK|Sm2e^`%(=U?Ipp?D(SY;AEsj)YwpajlzT`w#x_`X}qM;hg| z?_1c`HCw~3+_mGS5kGk^;pWRKBkO(veL^Jpj9vA&`;#i9`TO!18V<4jTO~I^{Jwu9sAa;`MMHadyVtDp|j} zH+!QNi$C{mZ>L={Ohq2f&ZAd2QYc7*MrzAU=hE*@uSH+qtB7MT;5CQ=bMJhmGF#ALr*1! zfiH%7tBc%4QLC$h#-6dfCSJ1MxGN`zN=uCANTrf@Bsc)w2V9dyUx(46)4ffR)FjRI zU-grGB};rky5}rPc)ads-@pg(GARp`@LrziEv-4k#$i#_Huby^X$%Rm}tdN zrMdmaM%BvlCu;=vFXC$ShyLp^IpH5B7ZKxb7t>FpHRQ6hmVo!nm)56Ja~~ca$ZFS# zj87hOLavdzVvF~1>6R9=%Px7bIsz;Jk~Q`Gu6KSfDt4HePCYzU@Yb+FLTkO^bSy?k zdBKf7r6E{tqMVBYek*?p}i9s%_*cC?vSCuV} zh>j*}Rp={pjxP2Of-8&Q3#+;~NPBXfyYNAxXVV`(oUp`4?uJ~MQZ3u|M{IMU8au** z@wd-An?X=(HwRu`ez)9mJ;CB~#fs9(86JM&^0Ik-J1s?{w z;-Z8T9vKT06&JTA;nM>el6Cd4_3%J9-kkBi_%YCqBhF!`QN8ucLxI+J)mm};vzps{&t^yN`Z_VN8w2yJ_>~op51Z)zXj zc=3nQ0W$|j(z@hL998MPNBgQ%$fv0K)E2Yoy;sK<&2=&+_7O5GE717(u&%rV*%dLj z{jSpG&m&b{?GowXuA9&8r(+T2thrKa5p`Wi$JqHLSF6*X7v?bAuV5}v_tzKNN| zC|c7-T}pkb+cN=tEeS42wlT#ilf1UIe}t8*_W8*3YhBUq3)|M)no78W2*28n zf8>$j)UXX~|3fZe*!wuXrn@&gyzp1uOy({=CMuC^!U-hxGB$?*wiazKq=}wWwV=7; zm3rRp^Zbre7(i~8TXUAaP<=K3YW~%4Vm*q%*RoNw_hOeKsT&`gG$g%DlN{%9vA5IR zyn*b@B=0gh=%DVDo`!pV-Baa;2sPH-u2wO* za(P92FEx0nTOiv$B(uajOIC$3l1d@o3}Vcv&j}Q2?UX@ci@1f+T&&Y-?9&L|epjBilPv z=PT@^PT-1AXyD#)5gK}xou02Ix$-g~5Zd4%AQX=>Kpc=VXM1iuU4l-l7)P24F(koj zp|)}IM+Qd5cIZP7W*&P8u?5$-z)C#fVF7)kA5$Dnm1MADvE8*LzP>WQl;_}Gotbp) zsNsG-bSJ4yO3F~7I@{S%A?Mv-z;nj3n>;BwkHfCfqAm29a&lCdxmjX9)cR-LgnCUk zv(L{P@)oW)g+pZ3*{g)PAS3xh&%C~@bTlCZ-5*iqE|xEJVeMnXI{;!F>z_cf=CS~~ z@eXk#sAh;cT)1c+g81%73v&3?&mTUQ5S1j(T$P`t{4)|%=xgD4h|`A6P-_NReMT)} z!k&~320Ml@=ePP~BvIOQYMxd>N9lcoZ`^!SE7eR3ez%A>BXq$ zae)R+4;HXm4Haz%V*h7v0YE*@A=v9H@F2U-or3&Xk$MwidgA*vft&Dg1W=|m$ULrt zW;xO+c0BRmzHXn?gRpZ-Tv*_eQ@HUXVO-I%7vBw8k_)rGKBs7u4Mrq{(ijwZ3_H++SHX7-?NK@fV)2b2Umg+Hze*Bu&DDj6gd~Q1H{B5*( zTcQ!{_v_Lb3J$ibaHm&#) zFxeqW4ox}V^f}J9o*9x_K9y>sn>sh6WX^QcT%xU1q&n&N1s=vfSWQx6=RD?1PP`EsQTs+u`jr!=A9`0}lK&$n>yQ@ZATKeN@SVBu z$CPoH4d_FSdHyMwMe9MR_Ivh3hZm6=Oo8&ih?kn99iv}K4lfv6$w@6w*oHa0?320? ziM*JP$ki{$ERW1xK!;P}SsY)%uD+MS@m_uSrq~1Z)v)m|UFw~Rr_*XyZr|VU&l<}; zSViEmjHki(=a0CSstec3kGXIVEzP%lV$>&y;ia!IqwcSml#;-L0t*%&Gphb7=6(CO zPG+Ab5FcsX<#Gxx1U|Su)TBr1Rd_3Gfs3BM9!wZF65@X7!GP17OU)86Xx%#Yrh5%TJW$n?r<; zjC(zySovZ>yFy%26>y`_QDY11=TKlYW|uONP8;I~7N&_@dq+#waTrVaBJpObG0GS7 zpxmntTl?l{?%{opwO6GMotqxb0S;u#^)9kyuhHh*rM;_c81>l{6Y|?b0e!^--f_?s zHr4H~M=75SoP5BKtDAH#)lhbNuO$2+0a?U!DOg})R2Iv>A* z+AnY_`MS+u1Yq#_ARkTnX?tS~a#ir0DL9^y< z$Yj}5A!F~8A)pj7g$3c0>KR71hS5`O&1@Z{r{nelr# z*?F@{N?yxp<|p&RnYY8kBWh|t{zf17ij01kr($H1lk(PDk2^N!wb$|n%cu8ZCJPFjURaI8$*>q)Hl>8+J zL57bzsspx|`3tmAXa-g~M1ZL1C%?jsyM+SfADwcBXv?nsppUzkQKq9qlhrQ}SmkxQ zhnr%tzdhFYq{lvdZ!ZiXFHhM(z*Scp+1WCx=%tsG(HxQlbgti3PkKpiL#CDrA$xRD z5yee>B7?>Xw;^+p*OP$Gz4I50c=*ROx+WUEsZUMUjIwWo<}UU#8E|23Q5WqbCEr#* za);FgDV`im9r{1_XS16)Ru~{Z+#Ek#|5~z&m{jgV>qn2wb{w33?yDIx-JQ4}^X-6!Yb2!A1 zTAqBI5f3kQUobX7!a=Xv*7&^B0_{T?zLgy( zw;J9$WRS_&Z-EPCB^FHOt$^mZBQ#pVA#h%)BKvpOg5^6!`tu79Qcq#@91>KH@d^j|{G2l84(fV4$4~UH^a2^_bozoABN*lS+Ru)_iA1tou9X z{F-jGTeqxC*T_e=&N9aP{AJeGw@LEy=A(6W<^^MRTE7%Fx3r9n^BZ(>UtW0k*_mo6 zPPthf@T}!K--xKkL$=JvZ~RZbX1dsCKe(8el9e^X$4BI=DEFv<`L0ZCnwDKiO%Fl! snNjBs=u;FHdD%PABBqajo}Oa>SYMbb>(6@I^ZTMzPumbtq3ICuKe;_5vH$=8 literal 0 HcmV?d00001 diff --git a/css/common.css b/css/common.css index 28dc6da..dbf3d34 100644 --- a/css/common.css +++ b/css/common.css @@ -14,7 +14,9 @@ --verypalegreen: #DFF5E3; --accentyellow: #feb70d; --nearblack: #18252a; - font-family: 'Clear Sans', 'Helvetica Neue', Arial, sans-serif; + font-family: 'Nunito', sans-serif; + /* font-family: 'Montserrat', sans-serif; */ + font-size: 14px; } @@ -31,3 +33,7 @@ color: white; } +/* Font application for all text */ +body, button, input, textarea, select, .sidebar, table { + font-family: var(--font-family), sans-serif; + } \ No newline at end of file diff --git a/data/law_dept_sample/personnel_data.json b/data/law_dept_sample/personnel_data.json index becc1ce..207b384 100644 --- a/data/law_dept_sample/personnel_data.json +++ b/data/law_dept_sample/personnel_data.json @@ -2,25 +2,19 @@ { "Job Name": "Deputy Counsel", "Account String": "1000-29320-320010", - "Current FTEs (FY25)": 1, - "Baseline FTEs": 0, - "Supplemental FTEs": 0, - "Current Average Salary": "150000" + "FY26 FTEs": 0, + "Average Projected Salary": "150000" }, { "Job Name": "Legal Secretary", "Account String": "1000-29320-320010", - "Current FTEs (FY25)": 5, - "Baseline FTEs": 0, - "Supplemental FTEs": 0, - "Current Average Salary": "55000" + "FY26 FTEs": 0, + "Average Projected Salary": "55000" }, { "Job Name": "Assistant Counsel", "Account String": "1000-29320-320010", - "Current FTEs (FY25)": 10, - "Baseline FTEs": 0, - "Supplemental FTEs": 0, - "Current Average Salary": "80000" + "FY26 FTEs": 0, + "Average Projected Salary": "80000" } ] \ No newline at end of file diff --git a/index.html b/index.html index 9357eab..28b495d 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,8 @@ + + diff --git a/js/components/body/body.css b/js/components/body/body.css index e0ba1a2..06770da 100644 --- a/js/components/body/body.css +++ b/js/components/body/body.css @@ -1,3 +1,4 @@ body { margin: 10px; + background-color: var(--lightGray); } \ No newline at end of file diff --git a/js/components/prompt/prompt.css b/js/components/prompt/prompt.css index ac7867e..ea8b3d8 100644 --- a/js/components/prompt/prompt.css +++ b/js/components/prompt/prompt.css @@ -18,11 +18,13 @@ } #option1:hover, #option2:hover { - background-color: var(--palegreen); - color: var(--citygreen); + background-color: var(--spiritgreen); + color: white; } #option2.clicked, #option1.clicked { font-weight: bold; - background-color: var(--palegreen); + background-color: var(--spiritgreen); + color: white; + border-width: 3; } diff --git a/js/components/table/table.css b/js/components/table/table.css index 61d69f7..f153336 100644 --- a/js/components/table/table.css +++ b/js/components/table/table.css @@ -8,6 +8,7 @@ th { tr { border-width: 2px; + background-color: white; } tr td { diff --git a/js/components/welcome/welcome.css b/js/components/welcome/welcome.css index e30c686..72f8475 100644 --- a/js/components/welcome/welcome.css +++ b/js/components/welcome/welcome.css @@ -6,21 +6,21 @@ font-size: 1.75em; margin-bottom: 0px; /* Adds spacing between buttons */ margin-left: 20%; + border-color: var(--citygreen); + border-width: 2; + color: var(--citygreen); + background-color: white; } -#step-upload { background-color: var(--blue);} -#step-initiatives { background-color: var(--orange); } -#step-revenue {background-color: var(--green);} -#step-personnel {background-color: var(--citygreen);} -#step-nonpersonnel {background-color: var(--yellow);} -#step-finish {background-color: var(--spiritgreen);} - .step:hover { color: white; - opacity: 50%; + background-color: var(--spiritgreen); } #welcome-page { justify-content: center; align-items: center; +} +.step.disabled { + opacity: 50%; } \ No newline at end of file diff --git a/js/init.js b/js/init.js index ed1d4a6..8c2de79 100644 --- a/js/init.js +++ b/js/init.js @@ -1,6 +1,6 @@ // import functions import { loadPageState } from './utils/data_utils/local_storage_handlers.js' -import { PAGES } from './views/view_logic.js' +import { visitPage } from './views/view_logic.js' // path for my laptop export let DATA_ROOT = '../../../data/law_dept_sample/' @@ -9,19 +9,15 @@ export let DATA_ROOT = '../../../data/law_dept_sample/' export let REVENUE = 0; export let TARGET = 2000000; +export var FISCAL_YEAR = '26'; // variables on the salary export var fringe = 0.36 export var cola = 0.02 export var merit = 0.02 + document.addEventListener('DOMContentLoaded', function () { var page_state = loadPageState(); - - // Use the page_state to access and call the corresponding function from PAGES - if (PAGES[page_state]) { - PAGES[page_state](); // Invokes the function if it exists in the PAGES map - } else { - console.error(`No page initializer found for state: ${page_state}`); - } + visitPage(page_state); }); \ No newline at end of file diff --git a/js/views/04_personnel/helpers.js b/js/views/04_personnel/helpers.js index 261a61b..338d932 100644 --- a/js/views/04_personnel/helpers.js +++ b/js/views/04_personnel/helpers.js @@ -1,22 +1,14 @@ -import Prompt from "../../components/prompt/prompt.js"; -import Table from '../../components/table/table.js' -import Sidebar from "../../components/sidebar/sidebar.js"; -import { DATA_ROOT, fringe, cola, merit } from "../../init.js" + +import { DATA_ROOT, FISCAL_YEAR, fringe, cola, merit } from "../../init.js" import Body from "../../components/body/body.js"; import NavButtons from "../../components/nav_buttons/nav_buttons.js"; import Subtitle from "../../components/header/header.js"; import Form from "../../components/form/form.js"; import Modal from "../../components/modal/modal.js"; +import Prompt from "../../components/prompt/prompt.js"; +import Table from '../../components/table/table.js' +import Sidebar from "../../components/sidebar/sidebar.js"; -const personnelColumns = [ - { title: 'Job Name', className: 'job-name' }, - { title: 'Baseline FTEs', className: 'baseline-ftes' }, - { title: 'Supplemental FTEs', className: 'supp-ftes' }, - { title: 'Service', className: 'service' }, - { title: 'Total Cost (Baseline)', className: 'total-baseline', isCost: true }, - { title: 'Total Cost (Supplementary)', className: 'total-supp', isCost: true }, - { title: 'Current Average Salary', className: 'avg-salary', isCost: true } -]; export function preparePageView(){ // prepare page view @@ -33,9 +25,22 @@ export function preparePageView(){ Prompt.Text.update('For each job in your department, select the service and request the number of baseline and supplemental FTEs.'); } +function assignClasses() { + // record columns and their classes + const personnelColumns = [ + { title: 'Job Name', className: 'job-name' }, + { title: `FY${FISCAL_YEAR} FTEs`, className: 'baseline-ftes' }, + { title: 'Service', className: 'service' }, + { title: 'Total Cost', className: 'total-baseline', isCost: true }, + { title: 'Average Projected Salary', className: 'avg-salary', isCost: true } + ]; + + // assign cost classes + Table.Columns.assignClasses(personnelColumns) +} + function personnelRowOnEdit(){ Table.Cell.createTextbox('baseline-ftes'); - Table.Cell.createTextbox('supp-ftes'); Table.Cell.createDropdown('service', DATA_ROOT + 'services.json'); } @@ -44,12 +49,10 @@ export async function initializePersonnelTable(){ await Table.Data.loadFromJSON(DATA_ROOT + 'personnel_data.json'); //after table is loaded, fill it Table.show(); - Table.Columns.add(3, '', 'Service'); - Table.Columns.addAtEnd('0', 'Total Cost (Baseline)'); - Table.Columns.addAtEnd( '0', 'Total Cost (Supplementary)'); - Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, ' '); - // assign cost classes - Table.Columns.assignClasses(personnelColumns); + Table.Columns.add(2, '', 'Service'); + Table.Columns.addAtEnd( '0', 'Total Cost'); + Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, ' ');; + assignClasses(); // activate edit buttons Table.Buttons.Edit.init(personnelRowOnEdit, updateDisplayandTotals); initializeRowAddition(); @@ -75,19 +78,13 @@ function updateDisplayandTotals(){ // fetch values for calculations let avg_salary = Table.Cell.getValue(rows[i], 'avg-salary'); let baseline_ftes = Table.Cell.getValue(rows[i], 'baseline-ftes'); - let supp_ftes = Table.Cell.getValue(rows[i], 'supp-ftes'); // calcuate #FTEs x average salary + COLA adjustments + merit adjustments + fringe let total_baseline_cost = calculateTotalCost(baseline_ftes, avg_salary, fringe, cola, merit); - let total_supp_cost = calculateTotalCost(supp_ftes, avg_salary, fringe, cola, merit); - // update counters + // update counter and total Sidebar.incrementStat('baseline-personnel', total_baseline_cost); - Sidebar.incrementStat('supp-personnel', total_supp_cost); - - // update totals in table Table.Cell.updateValue(rows[i], 'total-baseline', total_baseline_cost); - Table.Cell.updateValue(rows[i], 'total-supp', total_supp_cost); } } @@ -104,10 +101,6 @@ export function setUpForm() { Form.new('modal-body'); Form.NewField.shortText('Job Name:', 'job-name', true); Form.NewField.shortText('Account String:', 'account-string', true); - // Form.NewField.longText('Explain why this initiative is necessary and describe its potential impact.', 'Explanation', true); - // Form.NewField.numericInput('Estimate of ADDITONAL personnel cost?', 'Personnel Cost', true); - // Form.NewField.numericInput('Estimate of ADDITONAL nonpersonnel cost?', 'Non-personnel Cost', true); - // Form.NewField.numericInput('Estimate of TOTAL ADDITIONAL cost?', 'Total Cost', true); Form.SubmitButton.add(); // Initialize form submission to table data handleFormSubmissions(); diff --git a/js/views/04_personnel/main.js b/js/views/04_personnel/main.js index 8e39d92..ef2b481 100644 --- a/js/views/04_personnel/main.js +++ b/js/views/04_personnel/main.js @@ -1,4 +1,3 @@ - import { updatePageState } from "../../utils/data_utils/local_storage_handlers.js"; import { preparePageView, initializePersonnelTable, setUpModal, setUpForm } from "./helpers.js"; diff --git a/js/views/06_new_initiatives/helpers.js b/js/views/06_new_initiatives/helpers.js index 0add13e..5187d84 100644 --- a/js/views/06_new_initiatives/helpers.js +++ b/js/views/06_new_initiatives/helpers.js @@ -20,11 +20,7 @@ export function initializePageView() { Prompt.Buttons.Right.updateText('No'); // clicking 'no new initialitives' will also take us to the next page Prompt.Buttons.Right.addAction(pauseAndContinue); - Prompt.Buttons.Left.addAction(enableContinue); -} - -function enableContinue() { - NavButtons.Next.enable(); + Prompt.Buttons.Left.addAction(NavButtons.Next.enable); } export function setUpModal() { @@ -86,5 +82,5 @@ export function removeModalLinks(){ export function removePromptButtonListeners(){ Prompt.Buttons.Right.removeAction(pauseAndContinue); - Prompt.Buttons.Left.removeAction(enableContinue); + Prompt.Buttons.Left.removeAction(NavButtons.Next.enable); } \ No newline at end of file diff --git a/js/views/07_summary/main.js b/js/views/07_summary/main.js index 8de4dd7..82c8cc3 100644 --- a/js/views/07_summary/main.js +++ b/js/views/07_summary/main.js @@ -3,6 +3,7 @@ import Prompt from '../../components/prompt/prompt.js' import { initializeWelcomePage } from "../00_welcome/main.js"; import Body from "../../components/body/body.js"; import Subtitle from "../../components/header/header.js"; +import { visitPage } from "../view_logic.js"; export function loadSummaryPage(){ //update page state @@ -10,12 +11,19 @@ export function loadSummaryPage(){ // prepare page view Body.reset(); - Prompt.Buttons.Left.updateText('Submit'); + Prompt.Buttons.Left.updateText('Download Excel'); Prompt.Buttons.Right.updateText('Go back and edit'); // update page text Subtitle.update('Summary'); // TODO: update to make dynamic Prompt.Text.update(`Placeholder for summary and any issues.`); - Prompt.Buttons.Right.addAction(initializeWelcomePage); -} \ No newline at end of file + Prompt.Buttons.Right.addAction(returnToWelcome); +} + +export function cleanUpSummaryPage(){ + Prompt.Buttons.Right.removeAction(returnToWelcome); + console.log('attempting to clean up summary page') +} + +const returnToWelcome = () => {visitPage('welcome')} \ No newline at end of file diff --git a/js/views/view_logic.js b/js/views/view_logic.js index 5ffdfd2..24fce89 100644 --- a/js/views/view_logic.js +++ b/js/views/view_logic.js @@ -5,7 +5,7 @@ import { loadPersonnelPage } from './04_personnel/main.js'; import { loadOTPage } from './04.5_OT/main.js'; import { loadNonpersonnelPage } from './05_nonpersonnel/main.js'; import { loadBaselineLandingPage } from './02_baseline_landing_page/main.js'; -import { loadSummaryPage } from './07_summary/main.js'; +import { cleanUpSummaryPage, loadSummaryPage } from './07_summary/main.js'; import { loadUploadPage } from './01_upload/main.js'; import { pauseExecution } from '../utils/common_utils.js'; @@ -25,12 +25,27 @@ export let PAGES = { export let CLEANUP = { 'new-inits' : cleanUpInitiativesPage, - 'revenue' : cleanupRevenuePage + 'revenue' : cleanupRevenuePage, + 'summary' : cleanUpSummaryPage +} + +export function visitPage(new_page_key){ + // clean up from current page + var page_state = loadPageState(); + if (CLEANUP[page_state]) { CLEANUP[page_state]() }; + // Use the page_state to access and call the corresponding function from PAGES + if (PAGES[new_page_key]) { + PAGES[new_page_key](); // Invokes the function if it exists in the PAGES map + } else { + console.error(`No page initializer found for state: ${new_page_key}`); + } + PAGES[new_page_key](); } export function nextPage(){ var page_state = loadPageState(); + console.log(page_state); const keys = Object.keys(PAGES); // Find the index of the current key @@ -43,8 +58,8 @@ export function nextPage(){ if (currentIndex >= 0 && currentIndex < keys.length - 1) { // Get the next key const nextKey = keys[currentIndex + 1]; - const nextFn = PAGES[nextKey]; - nextFn(); + // go to that page + visitPage(nextKey); } } @@ -63,8 +78,8 @@ export function lastPage(){ if (currentIndex >= 1) { // Get the next key const lastKey = keys[currentIndex - 1]; - const lastFn = PAGES[lastKey]; - lastFn(); + // go to that page + visitPage(nextKey); } } From 4c7569ea751043942eae01f357a9ed4f1d8d08dc Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Wed, 3 Jul 2024 16:50:55 -0400 Subject: [PATCH 25/30] trying to fix table width and edit sidebar -- mostly unsuccessful --- data/law_dept_sample/nonpersonnel_data.json | 12 ++--- index.html | 49 +++++++++++---------- js/components/sidebar/sidebar.css | 24 ++++++++++ js/components/table/table.css | 9 ++-- js/views/05_nonpersonnel/helpers.js | 5 ++- js/views/view_logic.js | 2 +- 6 files changed, 65 insertions(+), 36 deletions(-) diff --git a/data/law_dept_sample/nonpersonnel_data.json b/data/law_dept_sample/nonpersonnel_data.json index 0f93cce..0b8493a 100644 --- a/data/law_dept_sample/nonpersonnel_data.json +++ b/data/law_dept_sample/nonpersonnel_data.json @@ -3,27 +3,27 @@ "Vendor": "Law Firm LLC", "CPA #" : "765421", "Account String": "1000-29320-320010", - "Object": "Consulting", + "Object Name": "Consulting", "End of Contract": "12/31/2024", "Amount Remaining" : 50000, - "Request Total": 100000 + "FY26 Request": 100000 }, { "Vendor": "Office Supplier", "CPA #" : "1234567", "Account String": "1000-29320-320010", - "Object": "Office Supplies", + "Object Name": "Office Supplies", "End of Contract": "12/31/2026", "Amount Remaining" : 500000, - "Request Total": 10000 + "FY26 Request": 10000 }, { "Vendor": "Software Co.", "CPA #" : "9876543", "Account String": "1000-29320-320010", - "Object": "Information Technology", + "Object Name": "Information Technology", "End of Contract": "6/1/2025", "Amount Remaining" : 30000, - "Request Total": 30000 + "FY26 Request": 30000 } ] \ No newline at end of file diff --git a/index.html b/index.html index 28b495d..ce73ef2 100644 --- a/index.html +++ b/index.html @@ -33,35 +33,36 @@ -

FY2026 Budget Form

-

- -
-
-
- -
- - - - -
-
-
+
+ + +

FY2026 Budget Form

+

+
+
+ + + +
+ + + + +
@@ -111,7 +112,7 @@

-