From cc75f4b2c16c1caf9c9f329ec2734d77a0dc6d81 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 17:41:28 -0400 Subject: [PATCH 01/22] no longer default to disabling next button --- src/js/components/nav_buttons/nav_buttons.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/components/nav_buttons/nav_buttons.js b/src/js/components/nav_buttons/nav_buttons.js index 4d9d362..2a5d6d8 100644 --- a/src/js/components/nav_buttons/nav_buttons.js +++ b/src/js/components/nav_buttons/nav_buttons.js @@ -9,7 +9,7 @@ function initializeNavButtons(){ // initialize next button const next_btn = document.getElementById('btn-next'); next_btn.addEventListener('click', nextPage); - disable('btn-next'); + // disable('btn-next'); } function hideNavButtons() { From a948b0ad7f35cc39bc9a514ccd738d8d2f07fc16 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 17:41:52 -0400 Subject: [PATCH 02/22] build view class --- src/js/views/view_class.js | 168 +++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 src/js/views/view_class.js diff --git a/src/js/views/view_class.js b/src/js/views/view_class.js new file mode 100644 index 0000000..d017414 --- /dev/null +++ b/src/js/views/view_class.js @@ -0,0 +1,168 @@ +import Prompt from "../components/prompt/prompt.js"; +import Sidebar from "../components/sidebar/sidebar.js"; +import NavButtons from "../components/nav_buttons/nav_buttons.js"; +import Body from "../components/body/body.js"; +import Subtitle from "../components/header/header.js"; +import Table from "../components/table/table.js"; +import Form from "../components/form/form.js"; +import Modal from "../components/modal/modal.js"; +import Sidebar from "../components/sidebar/sidebar.js"; + +export class View { + constructor(page_state) { + this.page_state = page_state + } + + navButtons() { return true } + + sidebar() { return true } + + form() { return null } + + prompt() { return '' } + + table() { return new ViewTable() } + + subtitle() { return '' } + + visit() { + // update page state + CurrentPage.update(this.page_state); + + // start with a blank page + Body.reset(); + + // default to showing navbuttons + if (this.navButtons()) { NavButtons.show(); }; + + // default to showing sidebar + if (this.sidebar()) { Sidebar.show() }; + + // initialize prompt text and buttons + if (this.prompt()) { Prompt.Text.update(this.prompt()) }; + + // initialize table + if (this.table()) { this.table(); } + + // show page subtitle + if (this.subtitle()) { Subtitle.update(this.subtitle()) }; + } + + cleanup() { return } + +} + +export class ViewTable { + + constructor(){ + // build out table from local storage + this.build(); + + // add the add new row button if needed + if (this.addButtonText()) { + this.setUpForm(); + } + } + + columns() { + // common columns in every table + const cols = [ + { title: 'Account String', className: 'account-string' }, + { title: 'Appropriation Name', className: 'approp-name', hide: true }, + { title: 'Appropriation', className: 'approp', hide: true }, + { title: 'Cost Center Name', className: 'cc-name', hide: true }, + { title: 'Cost Center', className: 'cc', hide: true }, + { title: 'Fund Name', className: 'fund-name', hide: true }, + { title: 'Fund', className: 'fund', hide: true }, + { title: 'Edit', className: 'edit' }, + ]; + return cols; + } + + async build() { + // build table from local storage and initialize edit buttons + + if(await Table.Data.load()) { + + //after table is loaded, show it + Table.show(); + + // add an edit column if needed + if (this.addEdit()) { + Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, 'Edit'); + // activate edit buttons + Table.Buttons.Edit.init(this.actionOnEdit, this.updateSidebar); + } + + // assign the correct classes based on the table columns + Table.Columns.assignClasses(this.columns()); + + // Apply any update function to make sure sidebar is up to date + this.updateSidebar(); + + } else { + + // show a message if there's no saved table data for the selected fund + if (noDataMessage()) { + Prompt.Text.update(noDataMessage()); + } + } + } + + // placeholder for action on row edit click + actionOnEdit() { return } + + // whether to add an edit column + addEdit() { return true }; + + // update function for the sidebar; default to just saving the table + updateSidebar() { Table.save() } + + // message to show if there's no saved data + noDataMessage() { return null }; + + // text to show for new row button + addButtonText() { return null }; + + addCustomQuestions() { return }; + + setUpForm() { + // set up modal for form when add button is pressed + Modal.clear(); + Modal.Link.add('add-btn'); + Modal.Title.update(addButtonText()); + + // create form + Form.new('modal-body'); + Form.SubmitButton.add(); + + // add custom questions + this.addCustomQuestions(); + + // Initialize form submission to table data + Modal.Submit.init(this.submitNewRow); + } + + submitNewRow(event) { + // get answers from form, hide form, show answers in table + const responses = Form.fetchAllResponses(event); + + // edit inputs from modal + responses = editColumns(responses); + + // 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.save(); + + // rebuild table + this.build(); + } + } + +} \ No newline at end of file From 44a069893ea3c44eca08d877c3b823f9ab196c0e Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 18:16:17 -0400 Subject: [PATCH 03/22] edit revenue to extend generic view class; removed a lot of redundancy! --- src/js/components/body/body.js | 5 +- src/js/components/nav_buttons/nav_buttons.js | 1 - src/js/views/03_revenue/main.js | 44 ++++++++-- src/js/views/view_class.js | 84 ++++++++++---------- 4 files changed, 83 insertions(+), 51 deletions(-) diff --git a/src/js/components/body/body.js b/src/js/components/body/body.js index 028bd84..37b688a 100644 --- a/src/js/components/body/body.js +++ b/src/js/components/body/body.js @@ -22,8 +22,9 @@ function resetPage() { Accordion.hide(); FileUpload.hide(); Tooltip.hide(); - // disable next button - NavButtons.Next.disable(); + + Table.adjustWidth('100%'); + Prompt.Buttons.reset(); // disable submit button Modal.Submit.deinit(); diff --git a/src/js/components/nav_buttons/nav_buttons.js b/src/js/components/nav_buttons/nav_buttons.js index 2a5d6d8..6e8b329 100644 --- a/src/js/components/nav_buttons/nav_buttons.js +++ b/src/js/components/nav_buttons/nav_buttons.js @@ -9,7 +9,6 @@ function initializeNavButtons(){ // initialize next button const next_btn = document.getElementById('btn-next'); next_btn.addEventListener('click', nextPage); - // disable('btn-next'); } function hideNavButtons() { diff --git a/src/js/views/03_revenue/main.js b/src/js/views/03_revenue/main.js index 36960e6..0d72c84 100644 --- a/src/js/views/03_revenue/main.js +++ b/src/js/views/03_revenue/main.js @@ -1,10 +1,44 @@ -import { CurrentPage } from '../../utils/data_utils/local_storage_handlers.js' -import { preparePageView } from './helpers.js' +import { View, ViewTable } from '../view_class.js' + +import Table from '../../components/table/table.js'; export function loadRevenuePage() { + var page = new Revenue(); + page.visit(); +} + +class Revenue extends View { + + constructor() { + super(); + this.page_state = 'revenue'; + this.prompt = 'Review and edit revenue line items.'; + this.subtitle = 'Revenues'; + this.table = new RevenueTable(); + } +} + +class RevenueTable extends ViewTable { + + constructor() { + super(); + + // add additional revenue columns to the table + this.columns = this.columns.concat([ + { title: 'Recurring or One-Time', className: 'recurring'}, + { title: 'Object Category', className: 'object-category'}, + { title: 'Departmental Request Total', className: 'request', isCost: true}, + { title: 'Departmental Request Notes', className: 'notes'}, + ]); + + this.noDataMessage = 'No revenues for this fund.' + } - //update page state - CurrentPage.update('revenue'); - preparePageView(); + // placeholder for action on row edit click + actionOnEdit() { + Table.Cell.createTextbox('request', true); + Table.Cell.createTextbox('notes'); + Table.Cell.createDropdown('recurring', ['One-Time', 'Recurring']); + } } diff --git a/src/js/views/view_class.js b/src/js/views/view_class.js index d017414..edc5f72 100644 --- a/src/js/views/view_class.js +++ b/src/js/views/view_class.js @@ -6,24 +6,28 @@ import Subtitle from "../components/header/header.js"; import Table from "../components/table/table.js"; import Form from "../components/form/form.js"; import Modal from "../components/modal/modal.js"; -import Sidebar from "../components/sidebar/sidebar.js"; -export class View { - constructor(page_state) { - this.page_state = page_state - } +import { CurrentPage } from "../utils/data_utils/local_storage_handlers.js"; - navButtons() { return true } +export class View { - sidebar() { return true } + constructor() { + // page state in local storage + this.page_state = ''; - form() { return null } + // whether to display + this.navButtons = true; + this.sidebar = true; - prompt() { return '' } + // text to show in the prompt area + this.prompt = null; - table() { return new ViewTable() } + // subtitle text + this.subtitle = ''; - subtitle() { return '' } + // table object of class ViewTable or null + this.table = null; + } visit() { // update page state @@ -33,19 +37,19 @@ export class View { Body.reset(); // default to showing navbuttons - if (this.navButtons()) { NavButtons.show(); }; + if (this.navButtons) { NavButtons.show(); }; // default to showing sidebar - if (this.sidebar()) { Sidebar.show() }; + if (this.sidebar) { Sidebar.show() }; // initialize prompt text and buttons - if (this.prompt()) { Prompt.Text.update(this.prompt()) }; + if (this.prompt) { Prompt.Text.update(this.prompt) }; // initialize table - if (this.table()) { this.table(); } + if (this.table) { this.table.build(); } // show page subtitle - if (this.subtitle()) { Subtitle.update(this.subtitle()) }; + if (this.subtitle) { Subtitle.update(this.subtitle) }; } cleanup() { return } @@ -55,18 +59,7 @@ export class View { export class ViewTable { constructor(){ - // build out table from local storage - this.build(); - - // add the add new row button if needed - if (this.addButtonText()) { - this.setUpForm(); - } - } - - columns() { - // common columns in every table - const cols = [ + this.columns = [ { title: 'Account String', className: 'account-string' }, { title: 'Appropriation Name', className: 'approp-name', hide: true }, { title: 'Appropriation', className: 'approp', hide: true }, @@ -76,26 +69,39 @@ export class ViewTable { { title: 'Fund', className: 'fund', hide: true }, { title: 'Edit', className: 'edit' }, ]; - return cols; + + // whether to add an edit column + this.addEdit = true ; + + // message to show if there's no saved data + this.noDataMessage = null; + + // text to show for new row button + this.addButtonText = null ; } async build() { // build table from local storage and initialize edit buttons + // add the add new row button if needed + if (this.addButtonText) { + this.setUpForm(); + } + if(await Table.Data.load()) { //after table is loaded, show it Table.show(); // add an edit column if needed - if (this.addEdit()) { + if (this.addEdit) { Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, 'Edit'); // activate edit buttons Table.Buttons.Edit.init(this.actionOnEdit, this.updateSidebar); } // assign the correct classes based on the table columns - Table.Columns.assignClasses(this.columns()); + Table.Columns.assignClasses(this.columns); // Apply any update function to make sure sidebar is up to date this.updateSidebar(); @@ -103,8 +109,8 @@ export class ViewTable { } else { // show a message if there's no saved table data for the selected fund - if (noDataMessage()) { - Prompt.Text.update(noDataMessage()); + if (this.noDataMessage) { + Prompt.Text.update(this.noDataMessage); } } } @@ -112,25 +118,17 @@ export class ViewTable { // placeholder for action on row edit click actionOnEdit() { return } - // whether to add an edit column - addEdit() { return true }; - // update function for the sidebar; default to just saving the table updateSidebar() { Table.save() } - // message to show if there's no saved data - noDataMessage() { return null }; - - // text to show for new row button - addButtonText() { return null }; - + // extra questions of the form to add a new row addCustomQuestions() { return }; setUpForm() { // set up modal for form when add button is pressed Modal.clear(); Modal.Link.add('add-btn'); - Modal.Title.update(addButtonText()); + Modal.Title.update(this.addButtonText); // create form Form.new('modal-body'); From fd3bd672040afef6492c1864120fe837758a0df0 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 22:12:28 -0400 Subject: [PATCH 04/22] adjust personnel view to new class --- src/js/views/03_revenue/main.js | 2 +- src/js/views/04_personnel/main.js | 101 ++++++++++++++++++++++++++++-- src/js/views/view_class.js | 37 ++++++++++- 3 files changed, 131 insertions(+), 9 deletions(-) diff --git a/src/js/views/03_revenue/main.js b/src/js/views/03_revenue/main.js index 0d72c84..7f0341b 100644 --- a/src/js/views/03_revenue/main.js +++ b/src/js/views/03_revenue/main.js @@ -34,7 +34,7 @@ class RevenueTable extends ViewTable { this.noDataMessage = 'No revenues for this fund.' } - // placeholder for action on row edit click + // action on row edit click: make cells editable actionOnEdit() { Table.Cell.createTextbox('request', true); Table.Cell.createTextbox('notes'); diff --git a/src/js/views/04_personnel/main.js b/src/js/views/04_personnel/main.js index e567206..8ad21d1 100644 --- a/src/js/views/04_personnel/main.js +++ b/src/js/views/04_personnel/main.js @@ -1,13 +1,104 @@ import { CurrentPage } from "../../utils/data_utils/local_storage_handlers.js"; import { preparePageView, initializePersonnelTable, setUpModal, setUpForm } from "./helpers.js"; +import { View, ViewTable } from '../view_class.js' + +import Table from "../../components/table/table.js"; +import Form from "../../components/form/form.js"; + +import { Services, FundLookupTable } from "../../utils/data_utils/budget_data_handlers.js"; +import { FISCAL_YEAR } from "../../init.js"; + +import { unformatCurrency } from "../../utils/common_utils.js"; + export function loadPersonnelPage(){ - CurrentPage.update('personnel'); - preparePageView(); - initializePersonnelTable(); + var page = new Personnel(); + page.visit(); - setUpModal(); - setUpForm(); } +class Personnel extends View { + + constructor() { + super(); + this.page_state = 'personnel'; + this.prompt = ` + This table displays the number of FTEs in each job code for in your department's + current (amended) FY25 budget. To make edits to the number of positions, click the + "Edit" button on the row you would like to edit. The "Total Cost" column and the + summary sidebar will also update to reflect any edits.`; + this.subtitle = 'Personnel'; + this.table = new PersonnelTable(); + } +} + + +class PersonnelTable extends ViewTable { + + constructor() { + super(); + + // add additional revenue columns to the table + this.columns = this.columns.concat([ + { title: 'Job Title', className: 'job-name' }, + { title: 'Service', className: 'service' }, + { title: `FY${FISCAL_YEAR} Requested FTE`, className: 'baseline-ftes' }, + { title: `FY${FISCAL_YEAR} Average Projected Salary/Wage`, className: 'avg-salary', isCost: true }, + { title: 'Total Cost', className: 'total-baseline', isCost: true }, + // hidden columns + { title: 'Fringe Benefits Rate', className: 'fringe', hide: true }, + { title: 'General Increase Rate', className: 'general-increase-rate', hide: true}, + { title: 'Step/Merit Increase Rate', className: 'merit-increase-rate', hide: true}, + { title: `Average Salary/Wage as of 9/1/20${FISCAL_YEAR-2}`, className: 'current-salary', isCost: true, hide: true} + ]); + + this.noDataMessage = 'No personnel expenditures for this fund.' + this.addButtonText = 'Add new job' ; + } + + // action on row edit click: make cells editable + actionOnEdit() { + Table.Cell.createTextbox('baseline-ftes'); + Table.Cell.createServiceDropdown(Services.list()); + } + + updateSidebar(){ + // calculate for each row + let rows = document.getElementsByTagName('tr'); + for (let i = 1; i < rows.length; i++){ + // fetch values for calculations + let avg_salary = Table.Cell.getValue(rows[i], 'avg-salary'); + let fringe = parseFloat(Table.Cell.getText(rows[i], 'fringe')); + let baseline_ftes = Table.Cell.getText(rows[i], 'baseline-ftes'); + + // calcuate #FTEs x average salary + COLA adjustments + merit adjustments + fringe + let total_baseline_cost = avg_salary * baseline_ftes * (1 + fringe); + + // update total column + Table.Cell.updateValue(rows[i], 'total-baseline', total_baseline_cost); + } + + // Save the table after all updates are done + Table.save(); + } + + addCustomQuestions(){ + // form questions to add a new job + Form.NewField.shortText('Job Title:', 'job-name', true); + Form.NewField.dropdown('Appropriation:', 'approp-name', FundLookupTable.getApprops(), true); + Form.NewField.dropdown('Cost Center:', 'cc-name', FundLookupTable.getCostCenters(), true); + Form.NewField.dropdown('Service', 'service', Services.list(), true); + Form.NewField.shortText('Number of FTEs requested:', 'baseline-ftes', true); + Form.NewField.shortText(`Projected average salary IN FISCAL YEAR ${FISCAL_YEAR}:`, 'avg-salary', true); + Form.NewField.shortText(`Expected fringe rate (as a percentage)`, 'fringe', true); + } + + editColumns(responses){ + // edit inputs from modal + responses['avg-salary'] = unformatCurrency(responses['avg-salary']); + responses = super.editColumns(responses); + responses['fringe'] = parseFloat(responses['fringe']) / 100; + return responses; + } +} \ No newline at end of file diff --git a/src/js/views/view_class.js b/src/js/views/view_class.js index edc5f72..787d8dd 100644 --- a/src/js/views/view_class.js +++ b/src/js/views/view_class.js @@ -8,6 +8,7 @@ import Form from "../components/form/form.js"; import Modal from "../components/modal/modal.js"; import { CurrentPage } from "../utils/data_utils/local_storage_handlers.js"; +import { AccountString } from "../utils/data_utils/budget_data_handlers.js"; export class View { @@ -59,6 +60,9 @@ export class View { export class ViewTable { constructor(){ + // Ensure methods retain the correct `this` context + this.submitNewRow = this.submitNewRow.bind(this); + this.columns = [ { title: 'Account String', className: 'account-string' }, { title: 'Appropriation Name', className: 'approp-name', hide: true }, @@ -125,6 +129,10 @@ export class ViewTable { addCustomQuestions() { return }; setUpForm() { + // show add button + Table.Buttons.AddRow.show(); + Table.Buttons.AddRow.updateText(this.addButtonText); + // set up modal for form when add button is pressed Modal.clear(); Modal.Link.add('add-btn'); @@ -132,21 +140,44 @@ export class ViewTable { // create form Form.new('modal-body'); - Form.SubmitButton.add(); // add custom questions this.addCustomQuestions(); + // add submit button + Form.SubmitButton.add(); // Initialize form submission to table data Modal.Submit.init(this.submitNewRow); } + editColumns(responses) { + // get numbers from account string names + if(responses['fund-name']){ + responses['fund'] = AccountString.getNumber(responses['fund-name']); + }; + if(responses['approp-name']){ + responses['approp'] = AccountString.getNumber(responses['approp-name']); + }; + if(responses['cc-name']){ + responses['cc'] = AccountString.getNumber(responses['cc-name']); + }; + if(responses['object-name']){ + responses['object'] = AccountString.getNumber(responses['object-name']); + }; + responses['account-string'] = + AccountString.build(responses['approp-name'], + responses['cc-name'], + responses['object-name'], + responses['fund-name']); + return responses; + } + submitNewRow(event) { // get answers from form, hide form, show answers in table - const responses = Form.fetchAllResponses(event); + var responses = Form.fetchAllResponses(event); // edit inputs from modal - responses = editColumns(responses); + responses = this.editColumns(responses); // make sure it's not an empty response if (Object.values(responses)[0] != ''){ From cc033d8f43a210bdc0e092e5e8b6015b4d1181a9 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 22:22:35 -0400 Subject: [PATCH 05/22] welcome page edit to match new view class --- src/js/views/00_welcome/main.js | 43 ++++++++++++++++++++++++++----- src/js/views/04_personnel/main.js | 3 --- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/js/views/00_welcome/main.js b/src/js/views/00_welcome/main.js index e4f4c11..538d1fb 100644 --- a/src/js/views/00_welcome/main.js +++ b/src/js/views/00_welcome/main.js @@ -1,11 +1,42 @@ -import { CurrentPage } from '../../utils/data_utils/local_storage_handlers.js' -import { initializePageView, addLinks } from './helpers.js' +import Welcome from '../../components/welcome/welcome.js'; + +import { loadUploadPage } from '../01_upload/main.js'; +import { loadNewInitiatives } from '../07_new_initiatives/main.js'; +import { loadBaselineLandingPage } from '../02_baseline_landing_page/main.js'; +import { loadSummaryPage } from '../08_summary/main.js'; + +import { View } from '../view_class.js'; export function initializeWelcomePage(){ - CurrentPage.update('welcome'); - initializePageView(); - addLinks(); + var page = new WelcomeView(); + page.visit(); + +} + +class WelcomeView extends View { + + constructor() { + super(); + this.page_state = 'welcome'; + this.subtitle = 'Welcome'; + this.sidebar = false; + this.navButtons = false; + } + + visit() { + super.visit(); + + // show welcome section + Welcome.show(); + + // 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) + + } -} \ No newline at end of file +} diff --git a/src/js/views/04_personnel/main.js b/src/js/views/04_personnel/main.js index 8ad21d1..9839d73 100644 --- a/src/js/views/04_personnel/main.js +++ b/src/js/views/04_personnel/main.js @@ -1,6 +1,3 @@ -import { CurrentPage } from "../../utils/data_utils/local_storage_handlers.js"; -import { preparePageView, initializePersonnelTable, setUpModal, setUpForm } from "./helpers.js"; - import { View, ViewTable } from '../view_class.js' import Table from "../../components/table/table.js"; From 1eef46c5584f2f818e5e21a5d3bbe77a7c9f232c Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 22:28:41 -0400 Subject: [PATCH 06/22] adjust file upload view --- src/js/views/01_upload/main.js | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/js/views/01_upload/main.js b/src/js/views/01_upload/main.js index 102fd45..07f55bb 100644 --- a/src/js/views/01_upload/main.js +++ b/src/js/views/01_upload/main.js @@ -1,9 +1,29 @@ -import { CurrentPage } from "../../utils/data_utils/local_storage_handlers.js"; -import { initializePageView } from "./helpers.js"; +import { View } from '../view_class.js' + +import FileUpload from "../../components/file_upload/file_upload.js"; +import NavButtons from "../../components/nav_buttons/nav_buttons.js"; export function loadUploadPage(){ - //update page state - CurrentPage.update('upload'); - initializePageView(); - -} \ No newline at end of file + var page = new UploadView(); + page.visit(); +} + +class UploadView extends View { + + constructor() { + super(); + this.page_state = 'upload'; + this.prompt = `Upload the baseline detail sheet given by your budget analyst.`; + this.subtitle = 'Excel Upload'; + this.sidebar = false; + } + + visit() { + super.visit(); + // disable continue button until Excel file is uploaded and read + NavButtons.Next.disable(); + FileUpload.show(); + FileUpload.init(); + NavButtons.Next.enable(); + } +} From 92d5be9f319cfd3e476b0d559469156f1d71e34d Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 22:56:30 -0400 Subject: [PATCH 07/22] update baseline to match new view class --- src/js/views/02_baseline_landing_page/main.js | 100 ++++++++++++++++-- 1 file changed, 93 insertions(+), 7 deletions(-) diff --git a/src/js/views/02_baseline_landing_page/main.js b/src/js/views/02_baseline_landing_page/main.js index 270c9ce..dbcd75e 100644 --- a/src/js/views/02_baseline_landing_page/main.js +++ b/src/js/views/02_baseline_landing_page/main.js @@ -1,10 +1,96 @@ -import { CurrentPage } from "../../utils/data_utils/local_storage_handlers.js"; -import { preparePageView, initializeFundTable } from "../02_baseline_landing_page/helpers.js"; - +import NavButtons from "../../components/nav_buttons/nav_buttons.js"; +import Table from "../../components/table/table.js"; +import { View, ViewTable } from '../view_class.js' +import { CurrentFund } from "../../utils/data_utils/local_storage_handlers.js"; export function loadBaselineLandingPage(){ - //update page state - CurrentPage.update('baseline-landing'); - preparePageView(); - initializeFundTable(); + + var page = new FundView(); + page.visit(); + +} + +class FundView extends View { + + constructor() { + super(); + this.page_state = 'baseline-landing'; + this.prompt = `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 then click continue.`; + this.subtitle = 'Baseline Budget Request'; + this.table = new FundTable(); + this.sidebar = false; + } + + visit() { + // remove fund selection + localStorage.setItem("fund", ''); + super.visit(); + } } + +class FundTable extends ViewTable { + + constructor() { + super(); + + // add additional revenue columns to the table + this.columns = [ + { title: 'Fund', className: 'fund-name' } + ]; + + this.noDataMessage = 'No funds found.' + this.addEdit = false; + } + + async build(){ + await super.build(); + + // Table.Data.loadFunds(); + // Table.show(); + NavButtons.Next.disable(); + // Table.Columns.assignClasses(this.columns); + + Table.adjustWidth('30%'); + allowRowSelection(); + } +} + + +function allowRowSelection(){ + + var tableRows = document.querySelectorAll("tbody tr"); + + // enable highlight on hover and on select + 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() { + selectFund(tableRows, this); + }); + }); +} + +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; + var fundNumber = parseInt(fund); + CurrentFund.update(fundNumber); + + // enable next step + NavButtons.Next.enable(); +} \ No newline at end of file From 036e4e5baf8d06df4cae0cec8c2f0626625d34b8 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 23:08:11 -0400 Subject: [PATCH 08/22] edit overtime to match view class --- src/js/views/04_personnel/main.js | 3 +- src/js/views/05_overtime/main.js | 100 ++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/js/views/04_personnel/main.js b/src/js/views/04_personnel/main.js index 9839d73..38325ea 100644 --- a/src/js/views/04_personnel/main.js +++ b/src/js/views/04_personnel/main.js @@ -36,7 +36,7 @@ class PersonnelTable extends ViewTable { constructor() { super(); - // add additional revenue columns to the table + // add additional personnel columns to the table this.columns = this.columns.concat([ { title: 'Job Title', className: 'job-name' }, { title: 'Service', className: 'service' }, @@ -92,6 +92,7 @@ class PersonnelTable extends ViewTable { } editColumns(responses){ + responses = super.editColumns(responses); // edit inputs from modal responses['avg-salary'] = unformatCurrency(responses['avg-salary']); responses = super.editColumns(responses); diff --git a/src/js/views/05_overtime/main.js b/src/js/views/05_overtime/main.js index 1be9a1b..7dede3a 100644 --- a/src/js/views/05_overtime/main.js +++ b/src/js/views/05_overtime/main.js @@ -1,10 +1,100 @@ -import { CurrentPage } from "../../utils/data_utils/local_storage_handlers.js"; -import { preparePageView } from './helpers.js'; + +import { View, ViewTable } from '../view_class.js' +import Table from '../../components/table/table.js'; +import Form from '../../components/form/form.js'; + +import { FundLookupTable, Services } from '../../utils/data_utils/budget_data_handlers.js'; +import { unformatCurrency } from '../../utils/common_utils.js'; export function loadOTPage(){ - //update page state - CurrentPage.update('overtime'); - preparePageView(); + + var page = new OvertimeView(); + page.visit(); +} + +class OvertimeView extends View { + + constructor() { + super(); + this.page_state = 'overtime'; + this.prompt = ` + Please see your baseline overtime / holiday pay / shift premiums in the table below. + Make any edits and continue.`; + this.subtitle = 'Overtime Estimates'; + this.table = new OvertimeTable(); + } +} + +class OvertimeTable extends ViewTable { + + constructor() { + super(); + + // add additional OT columns to the table + this.columns = this.columns.concat([ + { title: 'Service', className: 'service' }, + { title: 'Recurring or One-Time', className: 'recurring'}, + { title: 'Hourly Employee Overtime (Wages)', className: 'OT-wages', isCost: true }, + { title: 'Salaried Employee Overtime (Salary)', className: 'OT-salary', isCost: true }, + { title: 'Total Cost (including benefits)', className : 'total', isCost: true}, + // hidden columns + { title: 'FICA Rate', className: 'fica', hide: true}, + ]); + + this.noDataMessage = 'No overtime expenditures for this fund.' + this.addButtonText = 'Add new cost center' ; + } + + // action on row edit click: make cells editable + actionOnEdit() { + Table.Cell.createTextbox('OT-wages', true); + Table.Cell.createTextbox('OT-salary', true); + Table.Cell.createServiceDropdown(Services.list()); + Table.Cell.createDropdown('recurring', ['One-Time', 'Recurring']); + } + + updateSidebar(){ + + function calculateTotalCost(salary, wages, fica_rate){ + fica_rate = parseFloat(fica_rate); + return (wages + salary) * (1 + fica_rate) ; + } + + // calculate for each row + let rows = document.getElementsByTagName('tr'); + for (let i = 1; i < rows.length; i++){ + // fetch values for calculations + let OT_salary = Table.Cell.getValue(rows[i], 'OT-salary'); + let OT_wages = Table.Cell.getValue(rows[i], 'OT-wages'); + let fica_rate = Table.Cell.getText(rows[i], 'fica'); + + // add salary and wages and fringe benefits (FICA) + let row_total = calculateTotalCost(OT_salary, OT_wages, fica_rate); + + // update total + Table.Cell.updateValue(rows[i], 'total', row_total); + } + + // Save the table after all updates are done + Table.save(); + } + + addCustomQuestions(){ + // form questions to add a new job + Form.NewField.dropdown('Appropriation:', 'approp-name', FundLookupTable.getApprops(), true); + Form.NewField.dropdown('Cost Center:', 'cc-name', FundLookupTable.getCostCenters(), true); + Form.NewField.dropdown('Service', 'service', Services.list(), true); + Form.NewField.dropdown('Recurring or One-Time', 'recurring', ['Recurring', 'One-Time'], true); + Form.NewField.shortText('Overtime amount requested:', 'OT-wages', true); + } + + editColumns(responses){ + responses = super.editColumns(responses); + // edit inputs from modal + responses['OT-wages'] = unformatCurrency(responses['OT-wages']); + responses['fica'] = 0.0765; + return responses; + } } \ No newline at end of file From f45ebc09b75f372e98bc5d20eb70315ec1dc5215 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 23:22:18 -0400 Subject: [PATCH 09/22] create nonpersonnel class --- src/js/views/04_personnel/main.js | 2 +- src/js/views/05_overtime/main.js | 2 +- src/js/views/06_nonpersonnel/helpers.js | 2 +- src/js/views/06_nonpersonnel/main.js | 79 +++++++++++++++++++++++-- src/js/views/view_class.js | 6 +- 5 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/js/views/04_personnel/main.js b/src/js/views/04_personnel/main.js index 38325ea..5e7e82b 100644 --- a/src/js/views/04_personnel/main.js +++ b/src/js/views/04_personnel/main.js @@ -60,7 +60,7 @@ class PersonnelTable extends ViewTable { Table.Cell.createServiceDropdown(Services.list()); } - updateSidebar(){ + updateTable(){ // calculate for each row let rows = document.getElementsByTagName('tr'); for (let i = 1; i < rows.length; i++){ diff --git a/src/js/views/05_overtime/main.js b/src/js/views/05_overtime/main.js index 7dede3a..5aaf661 100644 --- a/src/js/views/05_overtime/main.js +++ b/src/js/views/05_overtime/main.js @@ -55,7 +55,7 @@ class OvertimeTable extends ViewTable { Table.Cell.createDropdown('recurring', ['One-Time', 'Recurring']); } - updateSidebar(){ + updateTable(){ function calculateTotalCost(salary, wages, fica_rate){ fica_rate = parseFloat(fica_rate); diff --git a/src/js/views/06_nonpersonnel/helpers.js b/src/js/views/06_nonpersonnel/helpers.js index c6ea724..6bdc5f1 100644 --- a/src/js/views/06_nonpersonnel/helpers.js +++ b/src/js/views/06_nonpersonnel/helpers.js @@ -25,7 +25,7 @@ const nonPersonnelColumns = [ { title: 'Cost Center Name', className: 'cc-name', hide: true }, { title: 'Appropriation', className: 'approp', hide: true }, { title: 'Cost Center', className: 'cc', hide: true }, - { title: 'Contract End Date', className: 'contract-end', hide:true}, + { title: 'Contract End Date', className: 'contract-end', hide: true}, { title: 'Amount Remaining on Contract', className: 'remaining', isCost: true , hide: true}, { title: 'Object Name', className: 'object-name', hide: true}, { title: 'Object', className: 'object', hide: true}, diff --git a/src/js/views/06_nonpersonnel/main.js b/src/js/views/06_nonpersonnel/main.js index 2976c97..47e7cac 100644 --- a/src/js/views/06_nonpersonnel/main.js +++ b/src/js/views/06_nonpersonnel/main.js @@ -1,9 +1,78 @@ -import { CurrentPage } from "../../utils/data_utils/local_storage_handlers.js"; -import { preparePageView, initializeNonpersonnelTable } from "./helpers.js"; +import { View, ViewTable } from '../view_class.js' +import Form from '../../components/form/form.js'; +import Table from '../../components/table/table.js'; +import { FundLookupTable } from '../../utils/data_utils/budget_data_handlers.js'; +import { ObjectCategories, Services } from '../../utils/data_utils/budget_data_handlers.js'; +import { unformatCurrency } from '../../utils/common_utils.js'; export function loadNonpersonnelPage(){ - CurrentPage.update('nonpersonnel'); - preparePageView(); - initializeNonpersonnelTable() + var page = new NonPersonnelView(); + page.visit(); + +} + +class NonPersonnelView extends View { + + constructor() { + super(); + this.page_state = 'nonpersonnel'; + this.prompt = 'Review and edit non-personnel line items.'; + this.subtitle = 'Non-Personnel'; + this.table = new NonPersonnelTable(); + } } + + +class NonPersonnelTable extends ViewTable { + + constructor() { + super(); + + // add additional personnel columns to the table + this.columns = this.columns.concat([ + { title: 'FY26 Request', className: 'request', isCost: true }, + { title: 'Service', className : 'service' }, + { title: 'Recurring or One-Time', className: 'recurring'}, + { title : 'CPA #', className : 'cpa'}, + // hidden columns + { title: 'Contract End Date', className: 'contract-end', hide: true}, + { title: 'Amount Remaining on Contract', className: 'remaining', isCost: true , hide: true}, + { title: 'Object Name', className: 'object-name', hide: true}, + { title: 'Object', className: 'object', hide: true}, + { title: 'Vendor Name', className: 'vendor', hide: true}, + { title: 'Object Category', className: 'object-category', hide: true}, + { title: 'BPA/CPA Description', className: 'cpa-description', hide: true} + ]); + + this.noDataMessage = 'No personnel expenditures for this fund.' + this.addButtonText = 'Add new job' ; + } + + // action on row edit click: make cells editable + actionOnEdit() { + Table.Cell.createTextbox('request', true); + Table.Cell.createServiceDropdown(); + Table.Cell.createDropdown('recurring', ['One-Time', 'Recurring']); + } + + addCustomQuestions(){ + // form questions to add a new row + Form.NewField.dropdown('Appropriation:', 'approp-name', FundLookupTable.getApprops(), true); + Form.NewField.dropdown('Cost Center:', 'cc-name', FundLookupTable.getCostCenters(), true); + Form.NewField.dropdown('Object Category:', 'object-category', ObjectCategories.list, true); + // TODO: maybe give dropdown based on selected obj category + Form.NewField.shortText('Object Number (if known):', 'object', false); + Form.NewField.dropdown('Service', 'service', Services.list(), true); + Form.NewField.longText('Describe your new request:', 'cpa-description', true); + Form.NewField.dropdown('Recurring or One-Time', 'recurring', ['Recurring', 'One-Time'], true); + Form.NewField.shortText('Amount requested:', 'request', true); + } + + editColumns(responses){ + responses = super.editColumns(responses); + responses['avg-salary'] = unformatCurrency(responses['avg-salary']); + responses['fringe'] = parseFloat(responses['fringe']) / 100; + return responses; + } +} \ No newline at end of file diff --git a/src/js/views/view_class.js b/src/js/views/view_class.js index 787d8dd..b895400 100644 --- a/src/js/views/view_class.js +++ b/src/js/views/view_class.js @@ -101,14 +101,14 @@ export class ViewTable { if (this.addEdit) { Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, 'Edit'); // activate edit buttons - Table.Buttons.Edit.init(this.actionOnEdit, this.updateSidebar); + Table.Buttons.Edit.init(this.actionOnEdit, this.updateTable); } // assign the correct classes based on the table columns Table.Columns.assignClasses(this.columns); // Apply any update function to make sure sidebar is up to date - this.updateSidebar(); + this.updateTable(); } else { @@ -123,7 +123,7 @@ export class ViewTable { actionOnEdit() { return } // update function for the sidebar; default to just saving the table - updateSidebar() { Table.save() } + updateTable() { Table.save() } // extra questions of the form to add a new row addCustomQuestions() { return }; From 4d781e127c4a424d787833c2568aec8ca3bed824 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Fri, 26 Jul 2024 23:55:20 -0400 Subject: [PATCH 10/22] attempting new init class, but running into a bug with the table loading --- src/js/views/02_baseline_landing_page/main.js | 14 +-- src/js/views/07_new_initiatives/main.js | 104 ++++++++++++++++-- src/js/views/view_class.js | 4 +- 3 files changed, 103 insertions(+), 19 deletions(-) diff --git a/src/js/views/02_baseline_landing_page/main.js b/src/js/views/02_baseline_landing_page/main.js index dbcd75e..001c396 100644 --- a/src/js/views/02_baseline_landing_page/main.js +++ b/src/js/views/02_baseline_landing_page/main.js @@ -44,15 +44,15 @@ class FundTable extends ViewTable { this.addEdit = false; } - async build(){ - await super.build(); + build(){ + // load in fund data + Table.Data.loadFunds(); + Table.show(); + Table.Columns.assignClasses(this.columns); + Table.adjustWidth('30%'); - // Table.Data.loadFunds(); - // Table.show(); + // disable next button until a fund is selected NavButtons.Next.disable(); - // Table.Columns.assignClasses(this.columns); - - Table.adjustWidth('30%'); allowRowSelection(); } } diff --git a/src/js/views/07_new_initiatives/main.js b/src/js/views/07_new_initiatives/main.js index 90bf05c..2942797 100644 --- a/src/js/views/07_new_initiatives/main.js +++ b/src/js/views/07_new_initiatives/main.js @@ -1,19 +1,103 @@ -import { initializePageView, setUpModal, setUpForm, removeModalLinks, removePromptButtonListeners, initializeInitTable } from './helpers.js' -import { CurrentPage } from '../../utils/data_utils/local_storage_handlers.js' +import { removeModalLinks, removePromptButtonListeners } from './helpers.js' +import { View, ViewTable } from '../view_class.js' +import Table from "../../components/table/table.js"; +import Form from "../../components/form/form.js"; +import { FundLookupTable } from "../../utils/data_utils/budget_data_handlers.js"; +import { FISCAL_YEAR } from "../../init.js"; +import Modal from '../../components/modal/modal.js'; +import { AccountString } from '../../utils/data_utils/budget_data_handlers.js'; + +const dropdownOptions = ['N/A', 'One-Time', 'Recurring'] // set up page and initialize all buttons export function loadNewInitiatives() { - CurrentPage.update('new-inits'); - initializePageView(); - setUpModal(); - setUpForm(); - initializeInitTable(); + var page = new InitiativesView(); + page.visit(); } export function cleanUpInitiativesPage() { - removeModalLinks(); - // remove event listeners on prompt buttons - removePromptButtonListeners(); + var page = new InitiativesView(); + page.cleanup(); +} + +class InitiativesView extends View { + + constructor() { + super(); + this.page_state = 'new-inits'; + this.prompt = ` + This is the place to propose new initiatives for FY${FISCAL_YEAR}. + New initiative submissions will count as supplemental line items and will be the starting + point for a conversation with both OB and ODFS, who will help with the details.`; + this.subtitle = 'New Initiatives'; + this.table = new InitiativesTable(); + } + + visit() { + super.visit(); + // remove fund selection + localStorage.setItem("fund", ''); + } + +} + +class InitiativesTable extends ViewTable { + + constructor() { + super(); + + // add additional columns to the table + this.columns = this.columns.concat([ + { title: 'Initiative Name', className: 'init-name' }, + { title: 'Ballpark Total Expenses', className: 'total', isCost: true }, + { title: 'Personnel Cost', className: 'personnel', isCost: true }, + { title: 'Non-personnel Cost', className: 'nonpersonnel', isCost: true }, + { title: 'Revenue', className: 'revenue', isCost: true }, + { title: 'Revenue Type', className: 'rev-type' }, + + // hide the explanation columns + { title: 'Q1', className: 'q1', hide: true }, + { title: 'Q2', className: 'q2', hide: true }, + { title: 'Q3', className: 'q3', hide: true } + ]); + + this.addButtonText = 'Add new initiative' ; + } + + addCustomQuestions(){ + + // general questions + Form.NewField.shortText('Initiative Name:', 'init-name', true); + Form.NewField.longText('What is the business case for the Initiative?', 'q1', true); + Form.NewField.longText(`Why is the initiative needed? What is the value-add to residents? + What is the Department’s plan for implementing the Initiative?`, 'q2', true); + Form.NewField.longText(`Why can’t the Initiative be funded with the Department’s baseline budget?`, 'q3', true); + + // TODO: Edit to drop down + Form.NewField.dropdown('Fund:', 'fund-name', FundLookupTable.listFundNames(), true); + Form.NewField.dropdown('Appropriation (if known):', 'approp-name', FundLookupTable.getApprops(), true); + Form.NewField.dropdown('Cost Center (if known):', 'cc-name', FundLookupTable.getCostCenters(), true); + + // Numbers + Form.NewField.numericInput('What is your ballpark estimate of TOTAL ADDITONAL expenses associated with this initiative?', + 'total', false); + Form.NewField.numericInput('Estimate of ADDITONAL personnel cost?', 'personnel', false); + Form.NewField.numericInput('Estimate of ADDITONAL nonpersonnel cost?', 'nonpersonnel', false); + Form.NewField.numericInput('Estimate of ADDITONAL revenue (if applicable)?', 'revenue', false); + Form.NewField.dropdown(`If there will be revenue, is it one-time or recurring?`, + 'rev-type', dropdownOptions); + } + + // action on row edit click: make cells editable + actionOnEdit() { + Table.Cell.createTextbox('total', true); + Table.Cell.createTextbox('revenue', true); + Table.Cell.createTextbox('personnel', true); + Table.Cell.createTextbox('nonpersonnel', true); + Table.Cell.createTextbox('init-name'); + Table.Cell.createDropdown('rev-type', dropdownOptions); + } + } \ No newline at end of file diff --git a/src/js/views/view_class.js b/src/js/views/view_class.js index b895400..f944695 100644 --- a/src/js/views/view_class.js +++ b/src/js/views/view_class.js @@ -168,7 +168,7 @@ export class ViewTable { AccountString.build(responses['approp-name'], responses['cc-name'], responses['object-name'], - responses['fund-name']); + responses['fund']); return responses; } @@ -186,7 +186,7 @@ export class ViewTable { Modal.hide(); // add data to table - Table.Rows.add(responses); + Table.Rows.add(responses, this.columns); Table.save(); // rebuild table From 3689aba088471f9be06619cce37f1c02744e2a62 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Sat, 27 Jul 2024 11:53:23 -0400 Subject: [PATCH 11/22] fix new inits bug --- src/js/views/07_new_initiatives/main.js | 3 --- src/js/views/view_class.js | 9 ++++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/js/views/07_new_initiatives/main.js b/src/js/views/07_new_initiatives/main.js index 2942797..e21fe98 100644 --- a/src/js/views/07_new_initiatives/main.js +++ b/src/js/views/07_new_initiatives/main.js @@ -1,13 +1,10 @@ -import { removeModalLinks, removePromptButtonListeners } from './helpers.js' import { View, ViewTable } from '../view_class.js' import Table from "../../components/table/table.js"; import Form from "../../components/form/form.js"; import { FundLookupTable } from "../../utils/data_utils/budget_data_handlers.js"; import { FISCAL_YEAR } from "../../init.js"; -import Modal from '../../components/modal/modal.js'; -import { AccountString } from '../../utils/data_utils/budget_data_handlers.js'; const dropdownOptions = ['N/A', 'One-Time', 'Recurring'] diff --git a/src/js/views/view_class.js b/src/js/views/view_class.js index f944695..2e336f8 100644 --- a/src/js/views/view_class.js +++ b/src/js/views/view_class.js @@ -53,7 +53,9 @@ export class View { if (this.subtitle) { Subtitle.update(this.subtitle) }; } - cleanup() { return } + cleanup() { + if (this.table) { Table.clear() } + } } @@ -92,6 +94,11 @@ export class ViewTable { this.setUpForm(); } + // delete any residual data + // TODO: delete + Table.clear(); + + // fill with new data from local storage if(await Table.Data.load()) { //after table is loaded, show it From 9079e1fbcd7285b87f7b55baf2f8e2033a9c4e5c Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Sat, 27 Jul 2024 12:16:12 -0400 Subject: [PATCH 12/22] summary view built --- src/js/views/08_summary/main.js | 71 ++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/src/js/views/08_summary/main.js b/src/js/views/08_summary/main.js index 655c035..d564013 100644 --- a/src/js/views/08_summary/main.js +++ b/src/js/views/08_summary/main.js @@ -1,13 +1,72 @@ -import { CurrentPage } from "../../utils/data_utils/local_storage_handlers.js"; -import { summaryView, disablePromptButtons } from "./helpers.js"; +import { CurrentFund, Baseline } from "../../utils/data_utils/local_storage_handlers.js"; +import { TARGET } from '../../init.js'; +import { Accordion } from "../../components/accordion/accordion.js"; +import { visitPage } from "../view_logic.js"; +import { formatCurrency } from '../../utils/common_utils.js'; +import { View } from "../view_class.js"; +import Prompt from "../../components/prompt/prompt.js"; +import { downloadXLSX } from "../../utils/data_utils/XLSX_handlers.js"; export function loadSummaryPage(){ - //update page state - CurrentPage.update('summary'); - summaryView(); + var page = new SummaryView(); + page.visit(); } export function cleanUpSummaryPage(){ - disablePromptButtons(); + var page = new SummaryView(); + page.cleanup(); } +function compareToTarget(){ + const baseline = new Baseline; + if (baseline.total() <= TARGET){ + Prompt.Text.update(`Congrats! Your budget is below your target! + Edit any line items below or download your completed Excel.`); + } else { + Prompt.Text.update(`Your budget is above your target of ${formatCurrency(TARGET)}. + Please expand the summary table below and edit line items until you meet your target. + When you meet the target, you will be able to download the Excel sheet.`); + Prompt.Buttons.Right.disable(); + } + Prompt.show(); +} + +const returnToWelcome = () => {visitPage('welcome')}; + +class SummaryView extends View { + + constructor() { + super(); + this.page_state = 'summary'; + this.subtitle = 'Summary'; + this.sidebar = false; + } + + visit() { + super.visit(); + + // reset fund + CurrentFund.reset(); + + // show summary accordion + Accordion.build(); + Accordion.show(); + + // add prompt buttons + Prompt.Buttons.Right.updateText('Download Excel'); + Prompt.Buttons.Left.updateText('Start over with new Excel upload'); + // add button links + Prompt.Buttons.Left.addAction(returnToWelcome); + Prompt.Buttons.Right.addAction(downloadXLSX); + + // update prompt text depending on target matching + compareToTarget(); + } + + cleanup() { + // delete event listeners + Prompt.Buttons.Left.removeAction(returnToWelcome); + Prompt.Buttons.Right.removeAction(downloadXLSX); + Prompt.Buttons.Right.enable(); + } +} \ No newline at end of file From 6f6ea7439af2eafe020e90befc096d49eac8bcbc Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Sat, 27 Jul 2024 12:59:33 -0400 Subject: [PATCH 13/22] re-org-ed views folder; still need to work out some kinks with FISCAL_YEAR load and view_logic --- src/js/views/00_welcome.js | 32 ++++++++++++ .../views/{01_upload/main.js => 01_upload.js} | 15 +++--- .../main.js => 02_baseline_landing.js} | 21 +++----- .../{03_revenue/main.js => 03_revenue.js} | 12 ++--- .../{04_personnel/main.js => 04_personnel.js} | 26 ++++------ .../{05_overtime/main.js => 05_overtime.js} | 23 ++++----- .../main.js => 06_nonpersonnel.js} | 26 ++++------ .../main.js => 07_new_initiatives.js} | 26 ++++------ .../{08_summary/main.js => 08_summary.js} | 34 +++++-------- .../views/{ => archive}/00_welcome/helpers.js | 6 +-- src/js/views/{ => archive}/00_welcome/main.js | 6 +-- .../views/{ => archive}/01_upload/helpers.js | 10 ++-- .../02_baseline_landing_page/helpers.js | 12 ++--- .../views/{ => archive}/03_revenue/helpers.js | 14 ++--- .../{ => archive}/04_personnel/helpers.js | 0 .../{ => archive}/05_overtime/helpers.js | 22 ++++---- .../{ => archive}/06_nonpersonnel/helpers.js | 24 ++++----- .../07_new_initiatives/helpers.js | 20 ++++---- .../views/{ => archive}/08_summary/helpers.js | 0 src/js/views/view_logic.js | 51 +++++++++---------- 20 files changed, 181 insertions(+), 199 deletions(-) create mode 100644 src/js/views/00_welcome.js rename src/js/views/{01_upload/main.js => 01_upload.js} (61%) rename src/js/views/{02_baseline_landing_page/main.js => 02_baseline_landing.js} (85%) rename src/js/views/{03_revenue/main.js => 03_revenue.js} (82%) rename src/js/views/{04_personnel/main.js => 04_personnel.js} (87%) rename src/js/views/{05_overtime/main.js => 05_overtime.js} (88%) rename src/js/views/{06_nonpersonnel/main.js => 06_nonpersonnel.js} (83%) rename src/js/views/{07_new_initiatives/main.js => 07_new_initiatives.js} (87%) rename src/js/views/{08_summary/main.js => 08_summary.js} (68%) rename src/js/views/{ => archive}/00_welcome/helpers.js (82%) rename src/js/views/{ => archive}/00_welcome/main.js (86%) rename src/js/views/{ => archive}/01_upload/helpers.js (60%) rename src/js/views/{ => archive}/02_baseline_landing_page/helpers.js (81%) rename src/js/views/{ => archive}/03_revenue/helpers.js (81%) rename src/js/views/{ => archive}/04_personnel/helpers.js (100%) rename src/js/views/{ => archive}/05_overtime/helpers.js (87%) rename src/js/views/{ => archive}/06_nonpersonnel/helpers.js (86%) rename src/js/views/{ => archive}/07_new_initiatives/helpers.js (90%) rename src/js/views/{ => archive}/08_summary/helpers.js (100%) diff --git a/src/js/views/00_welcome.js b/src/js/views/00_welcome.js new file mode 100644 index 0000000..7a97ae8 --- /dev/null +++ b/src/js/views/00_welcome.js @@ -0,0 +1,32 @@ + +import Welcome from '../components/welcome/welcome.js'; +import { View } from './view_class.js'; +import { visitPage } from './view_logic.js'; + +export class WelcomeView extends View { + + constructor() { + super(); + this.page_state = 'welcome'; + this.subtitle = 'Welcome'; + this.sidebar = false; + this.navButtons = false; + } + + visit() { + super.visit(); + + // show welcome section + Welcome.show(); + + // initialize links in buttons + document.getElementById('step-upload').addEventListener('click', visitPage('upload')); + document.getElementById('step-initiatives').addEventListener('click', visitPage('new-inits')) + document.getElementById('step-revenue').addEventListener('click', visitPage('revenue')) + document.getElementById('step-finish').addEventListener('click', visitPage('summary')) + + } + +} + +export default WelcomeView; diff --git a/src/js/views/01_upload/main.js b/src/js/views/01_upload.js similarity index 61% rename from src/js/views/01_upload/main.js rename to src/js/views/01_upload.js index 07f55bb..9285c74 100644 --- a/src/js/views/01_upload/main.js +++ b/src/js/views/01_upload.js @@ -1,14 +1,9 @@ -import { View } from '../view_class.js' +import { View } from './view_class.js' -import FileUpload from "../../components/file_upload/file_upload.js"; -import NavButtons from "../../components/nav_buttons/nav_buttons.js"; +import FileUpload from "../components/file_upload/file_upload.js"; +import NavButtons from "../components/nav_buttons/nav_buttons.js"; -export function loadUploadPage(){ - var page = new UploadView(); - page.visit(); -} - -class UploadView extends View { +export class UploadView extends View { constructor() { super(); @@ -27,3 +22,5 @@ class UploadView extends View { NavButtons.Next.enable(); } } + +export default UploadView; \ No newline at end of file diff --git a/src/js/views/02_baseline_landing_page/main.js b/src/js/views/02_baseline_landing.js similarity index 85% rename from src/js/views/02_baseline_landing_page/main.js rename to src/js/views/02_baseline_landing.js index 001c396..c72af89 100644 --- a/src/js/views/02_baseline_landing_page/main.js +++ b/src/js/views/02_baseline_landing.js @@ -1,16 +1,9 @@ -import NavButtons from "../../components/nav_buttons/nav_buttons.js"; -import Table from "../../components/table/table.js"; -import { View, ViewTable } from '../view_class.js' -import { CurrentFund } from "../../utils/data_utils/local_storage_handlers.js"; +import NavButtons from "../components/nav_buttons/nav_buttons.js"; +import Table from "../components/table/table.js"; +import { View, ViewTable } from './view_class.js' +import { CurrentFund } from "../utils/data_utils/local_storage_handlers.js"; -export function loadBaselineLandingPage(){ - - var page = new FundView(); - page.visit(); - -} - -class FundView extends View { +export class FundView extends View { constructor() { super(); @@ -93,4 +86,6 @@ function selectFund(tableRows, selected_row){ // enable next step NavButtons.Next.enable(); -} \ No newline at end of file +} + +export default FundView; \ No newline at end of file diff --git a/src/js/views/03_revenue/main.js b/src/js/views/03_revenue.js similarity index 82% rename from src/js/views/03_revenue/main.js rename to src/js/views/03_revenue.js index 7f0341b..9b2b172 100644 --- a/src/js/views/03_revenue/main.js +++ b/src/js/views/03_revenue.js @@ -1,13 +1,8 @@ -import { View, ViewTable } from '../view_class.js' +import { View, ViewTable } from './view_class.js' -import Table from '../../components/table/table.js'; +import Table from '../components/table/table.js'; -export function loadRevenuePage() { - var page = new Revenue(); - page.visit(); -} - -class Revenue extends View { +export class RevenueView extends View { constructor() { super(); @@ -42,3 +37,4 @@ class RevenueTable extends ViewTable { } } +export default RevenueView; \ No newline at end of file diff --git a/src/js/views/04_personnel/main.js b/src/js/views/04_personnel.js similarity index 87% rename from src/js/views/04_personnel/main.js rename to src/js/views/04_personnel.js index 5e7e82b..2f3c480 100644 --- a/src/js/views/04_personnel/main.js +++ b/src/js/views/04_personnel.js @@ -1,21 +1,14 @@ -import { View, ViewTable } from '../view_class.js' +import { View, ViewTable } from './view_class.js' -import Table from "../../components/table/table.js"; -import Form from "../../components/form/form.js"; +import Table from "../components/table/table.js"; +import Form from "../components/form/form.js"; -import { Services, FundLookupTable } from "../../utils/data_utils/budget_data_handlers.js"; -import { FISCAL_YEAR } from "../../init.js"; +import { Services, FundLookupTable } from "../utils/data_utils/budget_data_handlers.js"; +import { FISCAL_YEAR } from "../init.js"; -import { unformatCurrency } from "../../utils/common_utils.js"; +import { unformatCurrency } from "../utils/common_utils.js"; -export function loadPersonnelPage(){ - - var page = new Personnel(); - page.visit(); - -} - -class Personnel extends View { +export class PersonnelView extends View { constructor() { super(); @@ -95,8 +88,9 @@ class PersonnelTable extends ViewTable { responses = super.editColumns(responses); // edit inputs from modal responses['avg-salary'] = unformatCurrency(responses['avg-salary']); - responses = super.editColumns(responses); responses['fringe'] = parseFloat(responses['fringe']) / 100; return responses; } -} \ No newline at end of file +} + +export default PersonnelView; \ No newline at end of file diff --git a/src/js/views/05_overtime/main.js b/src/js/views/05_overtime.js similarity index 88% rename from src/js/views/05_overtime/main.js rename to src/js/views/05_overtime.js index 5aaf661..b302765 100644 --- a/src/js/views/05_overtime/main.js +++ b/src/js/views/05_overtime.js @@ -1,20 +1,13 @@ -import { View, ViewTable } from '../view_class.js' -import Table from '../../components/table/table.js'; -import Form from '../../components/form/form.js'; +import { View, ViewTable } from './view_class.js' +import Table from '../components/table/table.js'; +import Form from '../components/form/form.js'; -import { FundLookupTable, Services } from '../../utils/data_utils/budget_data_handlers.js'; -import { unformatCurrency } from '../../utils/common_utils.js'; +import { FundLookupTable, Services } from '../utils/data_utils/budget_data_handlers.js'; +import { unformatCurrency } from '../utils/common_utils.js'; -export function loadOTPage(){ - - var page = new OvertimeView(); - page.visit(); - -} - -class OvertimeView extends View { +export class OvertimeView extends View { constructor() { super(); @@ -97,4 +90,6 @@ class OvertimeTable extends ViewTable { responses['fica'] = 0.0765; return responses; } -} \ No newline at end of file +} + +export default OvertimeTable; \ No newline at end of file diff --git a/src/js/views/06_nonpersonnel/main.js b/src/js/views/06_nonpersonnel.js similarity index 83% rename from src/js/views/06_nonpersonnel/main.js rename to src/js/views/06_nonpersonnel.js index 47e7cac..61a3383 100644 --- a/src/js/views/06_nonpersonnel/main.js +++ b/src/js/views/06_nonpersonnel.js @@ -1,18 +1,11 @@ -import { View, ViewTable } from '../view_class.js' -import Form from '../../components/form/form.js'; -import Table from '../../components/table/table.js'; -import { FundLookupTable } from '../../utils/data_utils/budget_data_handlers.js'; -import { ObjectCategories, Services } from '../../utils/data_utils/budget_data_handlers.js'; -import { unformatCurrency } from '../../utils/common_utils.js'; +import { View, ViewTable } from './view_class.js' +import Form from '../components/form/form.js'; +import Table from '../components/table/table.js'; +import { FundLookupTable } from '../utils/data_utils/budget_data_handlers.js'; +import { ObjectCategories, Services } from '../utils/data_utils/budget_data_handlers.js'; +import { unformatCurrency } from '../utils/common_utils.js'; -export function loadNonpersonnelPage(){ - - var page = new NonPersonnelView(); - page.visit(); - -} - -class NonPersonnelView extends View { +export class NonPersonnelView extends View { constructor() { super(); @@ -23,7 +16,6 @@ class NonPersonnelView extends View { } } - class NonPersonnelTable extends ViewTable { constructor() { @@ -75,4 +67,6 @@ class NonPersonnelTable extends ViewTable { responses['fringe'] = parseFloat(responses['fringe']) / 100; return responses; } -} \ No newline at end of file +} + +export default NonPersonnelView; \ No newline at end of file diff --git a/src/js/views/07_new_initiatives/main.js b/src/js/views/07_new_initiatives.js similarity index 87% rename from src/js/views/07_new_initiatives/main.js rename to src/js/views/07_new_initiatives.js index e21fe98..1f27cf8 100644 --- a/src/js/views/07_new_initiatives/main.js +++ b/src/js/views/07_new_initiatives.js @@ -1,25 +1,15 @@ -import { View, ViewTable } from '../view_class.js' -import Table from "../../components/table/table.js"; -import Form from "../../components/form/form.js"; -import { FundLookupTable } from "../../utils/data_utils/budget_data_handlers.js"; -import { FISCAL_YEAR } from "../../init.js"; +import { View, ViewTable } from './view_class.js' +import Table from "../components/table/table.js"; +import Form from "../components/form/form.js"; +import { FundLookupTable } from "../utils/data_utils/budget_data_handlers.js"; +import { FISCAL_YEAR } from "../init.js"; const dropdownOptions = ['N/A', 'One-Time', 'Recurring'] // set up page and initialize all buttons -export function loadNewInitiatives() { - var page = new InitiativesView(); - page.visit(); -} - -export function cleanUpInitiativesPage() { - var page = new InitiativesView(); - page.cleanup(); -} - -class InitiativesView extends View { +export class InitiativesView extends View { constructor() { super(); @@ -97,4 +87,6 @@ class InitiativesTable extends ViewTable { Table.Cell.createDropdown('rev-type', dropdownOptions); } -} \ No newline at end of file +} + +export default InitiativesView; \ No newline at end of file diff --git a/src/js/views/08_summary/main.js b/src/js/views/08_summary.js similarity index 68% rename from src/js/views/08_summary/main.js rename to src/js/views/08_summary.js index d564013..d922be1 100644 --- a/src/js/views/08_summary/main.js +++ b/src/js/views/08_summary.js @@ -1,23 +1,13 @@ -import { CurrentFund, Baseline } from "../../utils/data_utils/local_storage_handlers.js"; -import { TARGET } from '../../init.js'; -import { Accordion } from "../../components/accordion/accordion.js"; -import { visitPage } from "../view_logic.js"; -import { formatCurrency } from '../../utils/common_utils.js'; -import { View } from "../view_class.js"; -import Prompt from "../../components/prompt/prompt.js"; -import { downloadXLSX } from "../../utils/data_utils/XLSX_handlers.js"; +import { CurrentFund, Baseline } from "../utils/data_utils/local_storage_handlers.js"; +import { TARGET } from '../init.js'; +import { Accordion } from "../components/accordion/accordion.js"; +import { visitPage } from "./view_logic.js"; +import { formatCurrency } from '../utils/common_utils.js'; +import { View } from "./view_class.js"; +import Prompt from "../components/prompt/prompt.js"; +import { downloadXLSX } from "../utils/data_utils/XLSX_handlers.js"; -export function loadSummaryPage(){ - var page = new SummaryView(); - page.visit(); -} - -export function cleanUpSummaryPage(){ - var page = new SummaryView(); - page.cleanup(); -} - -function compareToTarget(){ +export function compareToTarget(){ const baseline = new Baseline; if (baseline.total() <= TARGET){ Prompt.Text.update(`Congrats! Your budget is below your target! @@ -33,7 +23,7 @@ function compareToTarget(){ const returnToWelcome = () => {visitPage('welcome')}; -class SummaryView extends View { +export class SummaryView extends View { constructor() { super(); @@ -69,4 +59,6 @@ class SummaryView extends View { Prompt.Buttons.Right.removeAction(downloadXLSX); Prompt.Buttons.Right.enable(); } -} \ No newline at end of file +} + +export default SummaryView; \ No newline at end of file diff --git a/src/js/views/00_welcome/helpers.js b/src/js/views/archive/00_welcome/helpers.js similarity index 82% rename from src/js/views/00_welcome/helpers.js rename to src/js/views/archive/00_welcome/helpers.js index e8364d5..5e5b8a4 100644 --- a/src/js/views/00_welcome/helpers.js +++ b/src/js/views/archive/00_welcome/helpers.js @@ -1,6 +1,6 @@ -import Subtitle from '../../components/header/header.js' -import Welcome from '../../components/welcome/welcome.js' -import Body from '../../components/body/body.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 '../07_new_initiatives/main.js' import { loadSummaryPage } from '../08_summary/main.js' diff --git a/src/js/views/00_welcome/main.js b/src/js/views/archive/00_welcome/main.js similarity index 86% rename from src/js/views/00_welcome/main.js rename to src/js/views/archive/00_welcome/main.js index 538d1fb..ddc447e 100644 --- a/src/js/views/00_welcome/main.js +++ b/src/js/views/archive/00_welcome/main.js @@ -1,12 +1,12 @@ -import Welcome from '../../components/welcome/welcome.js'; +import Welcome from '../../../components/welcome/welcome.js'; import { loadUploadPage } from '../01_upload/main.js'; import { loadNewInitiatives } from '../07_new_initiatives/main.js'; import { loadBaselineLandingPage } from '../02_baseline_landing_page/main.js'; -import { loadSummaryPage } from '../08_summary/main.js'; +import { loadSummaryPage } from '../../08_summary.js'; -import { View } from '../view_class.js'; +import { View } from '../../view_class.js'; export function initializeWelcomePage(){ diff --git a/src/js/views/01_upload/helpers.js b/src/js/views/archive/01_upload/helpers.js similarity index 60% rename from src/js/views/01_upload/helpers.js rename to src/js/views/archive/01_upload/helpers.js index 292657f..a6c66b2 100644 --- a/src/js/views/01_upload/helpers.js +++ b/src/js/views/archive/01_upload/helpers.js @@ -1,8 +1,8 @@ -import Subtitle from '../../components/header/header.js' -import Prompt from '../../components/prompt/prompt.js' -import NavButtons from '../../components/nav_buttons/nav_buttons.js' -import Body from "../../components/body/body.js"; -import { FileUpload } from '../../components/file_upload/file_upload.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 Body from "../../../components/body/body.js"; +import { FileUpload } from '../../../components/file_upload/file_upload.js'; export function initializePageView() { diff --git a/src/js/views/02_baseline_landing_page/helpers.js b/src/js/views/archive/02_baseline_landing_page/helpers.js similarity index 81% rename from src/js/views/02_baseline_landing_page/helpers.js rename to src/js/views/archive/02_baseline_landing_page/helpers.js index fc90e45..83a1d3c 100644 --- a/src/js/views/02_baseline_landing_page/helpers.js +++ b/src/js/views/archive/02_baseline_landing_page/helpers.js @@ -1,10 +1,10 @@ -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 Body from "../../components/body/body.js"; -import { CurrentFund } 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 Body from "../../../components/body/body.js"; +import { CurrentFund } from '../../../utils/data_utils/local_storage_handlers.js'; const fundCols = [ { title: 'Fund', className: 'fund-name' }, diff --git a/src/js/views/03_revenue/helpers.js b/src/js/views/archive/03_revenue/helpers.js similarity index 81% rename from src/js/views/03_revenue/helpers.js rename to src/js/views/archive/03_revenue/helpers.js index a0f7d88..96cdc6b 100644 --- a/src/js/views/03_revenue/helpers.js +++ b/src/js/views/archive/03_revenue/helpers.js @@ -1,10 +1,10 @@ -import Prompt from '../../components/prompt/prompt.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 Sidebar from '../../components/sidebar/sidebar.js' -import Table from '../../components/table/table.js' -import Tooltip from '../../components/tooltip/tooltip.js' +import Prompt from '../../../components/prompt/prompt.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 Sidebar from '../../../components/sidebar/sidebar.js' +import Table from '../../../components/table/table.js' +import Tooltip from '../../../components/tooltip/tooltip.js' const revenueColumns = [ { title: 'Edit', className : 'edit' }, diff --git a/src/js/views/04_personnel/helpers.js b/src/js/views/archive/04_personnel/helpers.js similarity index 100% rename from src/js/views/04_personnel/helpers.js rename to src/js/views/archive/04_personnel/helpers.js diff --git a/src/js/views/05_overtime/helpers.js b/src/js/views/archive/05_overtime/helpers.js similarity index 87% rename from src/js/views/05_overtime/helpers.js rename to src/js/views/archive/05_overtime/helpers.js index 189878b..82bc56b 100644 --- a/src/js/views/05_overtime/helpers.js +++ b/src/js/views/archive/05_overtime/helpers.js @@ -1,15 +1,15 @@ -import Prompt from '../../components/prompt/prompt.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 Sidebar from '../../components/sidebar/sidebar.js'; -import Table from '../../components/table/table.js'; -import { AccountString, Services } from '../../utils/data_utils/budget_data_handlers.js'; -import Modal from '../../components/modal/modal.js'; -import Form from '../../components/form/form.js'; -import { unformatCurrency } from '../../utils/common_utils.js'; -import { FundLookupTable } from "../../utils/data_utils/budget_data_handlers.js"; +import Prompt from '../../../components/prompt/prompt.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 Sidebar from '../../../components/sidebar/sidebar.js'; +import Table from '../../../components/table/table.js'; +import { AccountString, Services } from '../../../utils/data_utils/budget_data_handlers.js'; +import Modal from '../../../components/modal/modal.js'; +import Form from '../../../components/form/form.js'; +import { unformatCurrency } from '../../../utils/common_utils.js'; +import { FundLookupTable } from "../../../utils/data_utils/budget_data_handlers.js"; export function preparePageView(){ diff --git a/src/js/views/06_nonpersonnel/helpers.js b/src/js/views/archive/06_nonpersonnel/helpers.js similarity index 86% rename from src/js/views/06_nonpersonnel/helpers.js rename to src/js/views/archive/06_nonpersonnel/helpers.js index 6bdc5f1..21aee91 100644 --- a/src/js/views/06_nonpersonnel/helpers.js +++ b/src/js/views/archive/06_nonpersonnel/helpers.js @@ -1,15 +1,15 @@ -import Prompt from "../../components/prompt/prompt.js"; -import Sidebar from "../../components/sidebar/sidebar.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 Subtitle from "../../components/header/header.js"; -import Tooltip from "../../components/tooltip/tooltip.js"; -import Modal from "../../components/modal/modal.js"; -import Form from "../../components/form/form.js"; -import { ObjectCategories, Services, AccountString } from "../../utils/data_utils/budget_data_handlers.js"; -import { FundLookupTable } from "../../utils/data_utils/budget_data_handlers.js"; -import { unformatCurrency } from "../../utils/common_utils.js"; +import Prompt from "../../../components/prompt/prompt.js"; +import Sidebar from "../../../components/sidebar/sidebar.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 Subtitle from "../../../components/header/header.js"; +import Tooltip from "../../../components/tooltip/tooltip.js"; +import Modal from "../../../components/modal/modal.js"; +import Form from "../../../components/form/form.js"; +import { ObjectCategories, Services, AccountString } from "../../../utils/data_utils/budget_data_handlers.js"; +import { FundLookupTable } from "../../../utils/data_utils/budget_data_handlers.js"; +import { unformatCurrency } from "../../../utils/common_utils.js"; const nonPersonnelColumns = [ { title: 'FY26 Request', className: 'request', isCost: true }, diff --git a/src/js/views/07_new_initiatives/helpers.js b/src/js/views/archive/07_new_initiatives/helpers.js similarity index 90% rename from src/js/views/07_new_initiatives/helpers.js rename to src/js/views/archive/07_new_initiatives/helpers.js index a3d1723..1ef9477 100644 --- a/src/js/views/07_new_initiatives/helpers.js +++ b/src/js/views/archive/07_new_initiatives/helpers.js @@ -1,14 +1,14 @@ -import Prompt from '../../components/prompt/prompt.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 '../view_logic.js' -import Subtitle from '../../components/header/header.js' -import Sidebar from '../../components/sidebar/sidebar.js' -import { FundLookupTable, AccountString } from '../../utils/data_utils/budget_data_handlers.js' +import Prompt from '../../../components/prompt/prompt.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 '../../view_logic.js' +import Subtitle from '../../../components/header/header.js' +import Sidebar from '../../../components/sidebar/sidebar.js' +import { FundLookupTable, AccountString } from '../../../utils/data_utils/budget_data_handlers.js' const explanation = `New initiative submissions will count as supplemental line items and will be the starting point for a conversation with both OB and ODFS, who will help with the details.` diff --git a/src/js/views/08_summary/helpers.js b/src/js/views/archive/08_summary/helpers.js similarity index 100% rename from src/js/views/08_summary/helpers.js rename to src/js/views/archive/08_summary/helpers.js diff --git a/src/js/views/view_logic.js b/src/js/views/view_logic.js index 4d20432..83326da 100644 --- a/src/js/views/view_logic.js +++ b/src/js/views/view_logic.js @@ -1,39 +1,37 @@ -import { initializeWelcomePage } from './00_welcome/main.js'; -import { cleanUpInitiativesPage, loadNewInitiatives } from './07_new_initiatives/main.js' -import { loadRevenuePage, cleanupRevenuePage } from './03_revenue/main.js' -import { loadPersonnelPage } from './04_personnel/main.js'; -import { loadOTPage } from './05_overtime/main.js'; -import { loadNonpersonnelPage } from './06_nonpersonnel/main.js'; -import { loadBaselineLandingPage } from './02_baseline_landing_page/main.js'; -import { cleanUpSummaryPage, loadSummaryPage } from './08_summary/main.js'; -import { loadUploadPage } from './01_upload/main.js'; +import WelcomeView from './00_welcome.js'; +import UploadView from './01_upload.js'; +import FundView from './02_baseline_landing.js'; +import RevenueView from './03_revenue.js'; +import PersonnelView from './04_personnel.js'; +import OvertimeView from './05_overtime.js'; +import NonPersonnelView from './06_nonpersonnel.js'; +import InitiativesView from './07_new_initiatives.js'; +import SummaryView from './08_summary.js'; + import { CurrentPage, CurrentFund } from '../utils/data_utils/local_storage_handlers.js'; import { FundLookupTable } from '../utils/data_utils/budget_data_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 let CLEANUP = { - 'new-inits' : cleanUpInitiativesPage, - 'summary' : cleanUpSummaryPage + 'welcome' : new WelcomeView(), + 'upload' : new UploadView(), + 'baseline-landing' : new FundView(), + 'revenue' : new RevenueView(), + 'personnel' : new PersonnelView(), + 'overtime' : new OvertimeView(), + 'nonpersonnel' : new NonPersonnelView(), + 'new-inits' : new InitiativesView(), + 'summary' : new SummaryView() } export function visitPage(new_page_key){ // clean up from current page var page_state = CurrentPage.load(); - if (CLEANUP[page_state]) { CLEANUP[page_state]() }; + PAGES[page_state].cleanup(); + // 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 + // Invokes the function if it exists in the PAGES map + PAGES[new_page_key].visit(); } else { console.error(`No page initializer found for state: ${new_page_key}`); }} @@ -46,9 +44,6 @@ 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]() }; - // unless on personnel (which will go to overtime), return to summary if all funds are viewed const returnPages = ['revenue', 'nonpersonnel', 'new-inits', 'overtime']; if (!FundLookupTable.fundsLeft() && returnPages.includes(CurrentPage.load())) { From 3b026b4b667d0adf29845096faf6e43b627dd429 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 29 Jul 2024 12:18:23 -0400 Subject: [PATCH 14/22] fix fiscal year load issue --- src/js/init.js | 7 ++-- .../data_utils/local_storage_handlers.js | 4 +-- src/js/views/04_personnel.js | 18 +++++----- src/js/views/05_overtime.js | 2 +- src/js/views/06_nonpersonnel.js | 8 ++--- src/js/views/view_logic.js | 34 ++++++++++++------- 6 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/js/init.js b/src/js/init.js index 3be21f6..8033d91 100644 --- a/src/js/init.js +++ b/src/js/init.js @@ -1,15 +1,14 @@ // import styles import '../css/common.css'; -// import functions -import { CurrentPage } from './utils/data_utils/local_storage_handlers.js'; - // temporary hard-coding -export let REVENUE = 0; export let TARGET = 10000000; // Set to equal current fiscal year export var FISCAL_YEAR = '26'; +// import functions +import { CurrentPage } from './utils/data_utils/local_storage_handlers.js'; + // sheets to expect on detail sheet export const SHEETS = { 'FTE, Salary-Wage, & Benefits' : 'personnel' , diff --git a/src/js/utils/data_utils/local_storage_handlers.js b/src/js/utils/data_utils/local_storage_handlers.js index dd5e9b1..e310325 100644 --- a/src/js/utils/data_utils/local_storage_handlers.js +++ b/src/js/utils/data_utils/local_storage_handlers.js @@ -1,6 +1,6 @@ import { FISCAL_YEAR } from "../../init.js"; import Sidebar from "../../components/sidebar/sidebar.js"; -import { PAGES, visitPage } from "../../views/view_logic.js"; +import { initializePages, visitPage } from "../../views/view_logic.js"; import { fetchJSON } from "./JSON_data_handlers.js"; import { FundLookupTable } from "./budget_data_handlers.js"; import { convertToJSON } from "./JSON_data_handlers.js"; @@ -53,7 +53,7 @@ function deleteTable(name){ export async function deleteAllTables(){ var funds = await fetchJSON(DATA_ROOT + 'funds.json'); funds = funds.map((item) => { return item.Name }); - for (const page in PAGES){ + for (const page in initializePages()){ for(const i in funds){ deleteTable(`${page}_${funds[i]}`); } diff --git a/src/js/views/04_personnel.js b/src/js/views/04_personnel.js index 2f3c480..070be96 100644 --- a/src/js/views/04_personnel.js +++ b/src/js/views/04_personnel.js @@ -4,13 +4,11 @@ import Table from "../components/table/table.js"; import Form from "../components/form/form.js"; import { Services, FundLookupTable } from "../utils/data_utils/budget_data_handlers.js"; -import { FISCAL_YEAR } from "../init.js"; - import { unformatCurrency } from "../utils/common_utils.js"; export class PersonnelView extends View { - constructor() { + constructor(fiscal_year) { super(); this.page_state = 'personnel'; this.prompt = ` @@ -19,28 +17,28 @@ export class PersonnelView extends View { "Edit" button on the row you would like to edit. The "Total Cost" column and the summary sidebar will also update to reflect any edits.`; this.subtitle = 'Personnel'; - this.table = new PersonnelTable(); + this.table = new PersonnelTable(fiscal_year); } } class PersonnelTable extends ViewTable { - constructor() { + constructor(fiscal_year) { super(); - + this.fiscal_year = fiscal_year; // add additional personnel columns to the table this.columns = this.columns.concat([ { title: 'Job Title', className: 'job-name' }, { title: 'Service', className: 'service' }, - { title: `FY${FISCAL_YEAR} Requested FTE`, className: 'baseline-ftes' }, - { title: `FY${FISCAL_YEAR} Average Projected Salary/Wage`, className: 'avg-salary', isCost: true }, + { title: `FY${this.fiscal_year} Requested FTE`, className: 'baseline-ftes' }, + { title: `FY${this.fiscal_year} Average Projected Salary/Wage`, className: 'avg-salary', isCost: true }, { title: 'Total Cost', className: 'total-baseline', isCost: true }, // hidden columns { title: 'Fringe Benefits Rate', className: 'fringe', hide: true }, { title: 'General Increase Rate', className: 'general-increase-rate', hide: true}, { title: 'Step/Merit Increase Rate', className: 'merit-increase-rate', hide: true}, - { title: `Average Salary/Wage as of 9/1/20${FISCAL_YEAR-2}`, className: 'current-salary', isCost: true, hide: true} + { title: `Average Salary/Wage as of 9/1/20${this.fiscal_year-2}`, className: 'current-salary', isCost: true, hide: true} ]); this.noDataMessage = 'No personnel expenditures for this fund.' @@ -80,7 +78,7 @@ class PersonnelTable extends ViewTable { Form.NewField.dropdown('Cost Center:', 'cc-name', FundLookupTable.getCostCenters(), true); Form.NewField.dropdown('Service', 'service', Services.list(), true); Form.NewField.shortText('Number of FTEs requested:', 'baseline-ftes', true); - Form.NewField.shortText(`Projected average salary IN FISCAL YEAR ${FISCAL_YEAR}:`, 'avg-salary', true); + Form.NewField.shortText(`Projected average salary IN FISCAL YEAR ${this.fiscal_year}:`, 'avg-salary', true); Form.NewField.shortText(`Expected fringe rate (as a percentage)`, 'fringe', true); } diff --git a/src/js/views/05_overtime.js b/src/js/views/05_overtime.js index b302765..96a925d 100644 --- a/src/js/views/05_overtime.js +++ b/src/js/views/05_overtime.js @@ -92,4 +92,4 @@ class OvertimeTable extends ViewTable { } } -export default OvertimeTable; \ No newline at end of file +export default OvertimeView; \ No newline at end of file diff --git a/src/js/views/06_nonpersonnel.js b/src/js/views/06_nonpersonnel.js index 61a3383..93f8088 100644 --- a/src/js/views/06_nonpersonnel.js +++ b/src/js/views/06_nonpersonnel.js @@ -7,23 +7,23 @@ import { unformatCurrency } from '../utils/common_utils.js'; export class NonPersonnelView extends View { - constructor() { + constructor(fiscal_year) { super(); this.page_state = 'nonpersonnel'; this.prompt = 'Review and edit non-personnel line items.'; this.subtitle = 'Non-Personnel'; - this.table = new NonPersonnelTable(); + this.table = new NonPersonnelTable(fiscal_year); } } class NonPersonnelTable extends ViewTable { - constructor() { + constructor(fiscal_year) { super(); // add additional personnel columns to the table this.columns = this.columns.concat([ - { title: 'FY26 Request', className: 'request', isCost: true }, + { title: `FY${fiscal_year} Request`, className: 'request', isCost: true }, { title: 'Service', className : 'service' }, { title: 'Recurring or One-Time', className: 'recurring'}, { title : 'CPA #', className : 'cpa'}, diff --git a/src/js/views/view_logic.js b/src/js/views/view_logic.js index 83326da..5a5bb8c 100644 --- a/src/js/views/view_logic.js +++ b/src/js/views/view_logic.js @@ -10,20 +10,27 @@ import SummaryView from './08_summary.js'; import { CurrentPage, CurrentFund } from '../utils/data_utils/local_storage_handlers.js'; import { FundLookupTable } from '../utils/data_utils/budget_data_handlers.js'; +import { FISCAL_YEAR } from '../init.js'; -export let PAGES = { - 'welcome' : new WelcomeView(), - 'upload' : new UploadView(), - 'baseline-landing' : new FundView(), - 'revenue' : new RevenueView(), - 'personnel' : new PersonnelView(), - 'overtime' : new OvertimeView(), - 'nonpersonnel' : new NonPersonnelView(), - 'new-inits' : new InitiativesView(), - 'summary' : new SummaryView() +export function initializePages() { + const PAGES = { + 'welcome': new WelcomeView(), + 'upload': new UploadView(), + 'baseline-landing': new FundView(), + 'revenue': new RevenueView(), + 'personnel': new PersonnelView(FISCAL_YEAR), + 'overtime': new OvertimeView(), + 'nonpersonnel': new NonPersonnelView(FISCAL_YEAR), + 'new-inits': new InitiativesView(), + 'summary': new SummaryView(), + }; + return PAGES; } export function visitPage(new_page_key){ + + const PAGES = initializePages(); + // clean up from current page var page_state = CurrentPage.load(); PAGES[page_state].cleanup(); @@ -38,6 +45,8 @@ export function visitPage(new_page_key){ export function nextPage(){ + const PAGES = initializePages(); + var page_state = CurrentPage.load(); const keys = Object.keys(PAGES); @@ -72,15 +81,14 @@ export function nextPage(){ export function lastPage(){ + const PAGES = initializePages(); + var page_state = CurrentPage.load(); const keys = Object.keys(PAGES); // Find the index of the current key const currentIndex = keys.indexOf(page_state); - // clean up current page - if (CLEANUP[page_state]) { CLEANUP[page_state]() }; - // if on new-inits, circle back to fund selection if (CurrentPage.load() == 'new-inits'){ visitPage('baseline-landing'); From 31e333eea16c1d73a4bea3d65a281744b57a5bc3 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 29 Jul 2024 12:18:49 -0400 Subject: [PATCH 15/22] remove old versions of the views --- src/js/views/archive/00_welcome/helpers.js | 24 --- src/js/views/archive/00_welcome/main.js | 42 ----- src/js/views/archive/01_upload/helpers.js | 24 --- .../02_baseline_landing_page/helpers.js | 65 ------- src/js/views/archive/03_revenue/helpers.js | 65 ------- src/js/views/archive/04_personnel/helpers.js | 146 --------------- src/js/views/archive/05_overtime/helpers.js | 156 ---------------- .../views/archive/06_nonpersonnel/helpers.js | 130 -------------- .../archive/07_new_initiatives/helpers.js | 169 ------------------ src/js/views/archive/08_summary/helpers.js | 55 ------ 10 files changed, 876 deletions(-) delete mode 100644 src/js/views/archive/00_welcome/helpers.js delete mode 100644 src/js/views/archive/00_welcome/main.js delete mode 100644 src/js/views/archive/01_upload/helpers.js delete mode 100644 src/js/views/archive/02_baseline_landing_page/helpers.js delete mode 100644 src/js/views/archive/03_revenue/helpers.js delete mode 100644 src/js/views/archive/04_personnel/helpers.js delete mode 100644 src/js/views/archive/05_overtime/helpers.js delete mode 100644 src/js/views/archive/06_nonpersonnel/helpers.js delete mode 100644 src/js/views/archive/07_new_initiatives/helpers.js delete mode 100644 src/js/views/archive/08_summary/helpers.js diff --git a/src/js/views/archive/00_welcome/helpers.js b/src/js/views/archive/00_welcome/helpers.js deleted file mode 100644 index 5e5b8a4..0000000 --- a/src/js/views/archive/00_welcome/helpers.js +++ /dev/null @@ -1,24 +0,0 @@ -import Subtitle from '../../../components/header/header.js' -import Welcome from '../../../components/welcome/welcome.js' -import Body from '../../../components/body/body.js' - -import { loadNewInitiatives } from '../07_new_initiatives/main.js' -import { loadSummaryPage } from '../08_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 - Body.reset(); - Subtitle.update("Welcome"); - Welcome.show(); -} - -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/src/js/views/archive/00_welcome/main.js b/src/js/views/archive/00_welcome/main.js deleted file mode 100644 index ddc447e..0000000 --- a/src/js/views/archive/00_welcome/main.js +++ /dev/null @@ -1,42 +0,0 @@ - -import Welcome from '../../../components/welcome/welcome.js'; - -import { loadUploadPage } from '../01_upload/main.js'; -import { loadNewInitiatives } from '../07_new_initiatives/main.js'; -import { loadBaselineLandingPage } from '../02_baseline_landing_page/main.js'; -import { loadSummaryPage } from '../../08_summary.js'; - -import { View } from '../../view_class.js'; - -export function initializeWelcomePage(){ - - var page = new WelcomeView(); - page.visit(); - -} - -class WelcomeView extends View { - - constructor() { - super(); - this.page_state = 'welcome'; - this.subtitle = 'Welcome'; - this.sidebar = false; - this.navButtons = false; - } - - visit() { - super.visit(); - - // show welcome section - Welcome.show(); - - // 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/src/js/views/archive/01_upload/helpers.js b/src/js/views/archive/01_upload/helpers.js deleted file mode 100644 index a6c66b2..0000000 --- a/src/js/views/archive/01_upload/helpers.js +++ /dev/null @@ -1,24 +0,0 @@ -import Subtitle from '../../../components/header/header.js' -import Prompt from '../../../components/prompt/prompt.js' -import NavButtons from '../../../components/nav_buttons/nav_buttons.js' -import Body from "../../../components/body/body.js"; -import { FileUpload } from '../../../components/file_upload/file_upload.js'; - -export function initializePageView() { - - // remove fund selection - localStorage.setItem("fund", ''); - - // prepare page view - Body.reset(); - NavButtons.show(); - FileUpload.show(); - - // update page text - Subtitle.update('Excel Upload'); - Prompt.Text.update(`Upload the baseline detail sheet given by your budget analyst.`); - - // show and initialize file upload; enable continue after file saved in local storage - FileUpload.init(); - NavButtons.Next.enable(); -} \ No newline at end of file diff --git a/src/js/views/archive/02_baseline_landing_page/helpers.js b/src/js/views/archive/02_baseline_landing_page/helpers.js deleted file mode 100644 index 83a1d3c..0000000 --- a/src/js/views/archive/02_baseline_landing_page/helpers.js +++ /dev/null @@ -1,65 +0,0 @@ - -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 Body from "../../../components/body/body.js"; -import { CurrentFund } from '../../../utils/data_utils/local_storage_handlers.js'; - -const fundCols = [ - { title: 'Fund', className: 'fund-name' }, -]; - -export function preparePageView(){ - - CurrentFund.reset(); - - // prepare page view - Body.reset(); - 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 then click continue.`); -} - -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() { - selectFund(tableRows, this); - }); - }); -} - -export function initializeFundTable(){ - Table.Data.loadFunds(); - Table.adjustWidth('30%'); - 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; - var fundNumber = parseInt(fund); - CurrentFund.update(fundNumber); - // enable next step - NavButtons.Next.enable(); -} \ No newline at end of file diff --git a/src/js/views/archive/03_revenue/helpers.js b/src/js/views/archive/03_revenue/helpers.js deleted file mode 100644 index 96cdc6b..0000000 --- a/src/js/views/archive/03_revenue/helpers.js +++ /dev/null @@ -1,65 +0,0 @@ -import Prompt from '../../../components/prompt/prompt.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 Sidebar from '../../../components/sidebar/sidebar.js' -import Table from '../../../components/table/table.js' -import Tooltip from '../../../components/tooltip/tooltip.js' - -const revenueColumns = [ - { title: 'Edit', className : 'edit' }, - { title : 'Account String', className : 'account-string'}, - { title: 'Recurring or One-Time', className: 'recurring'}, - { title: 'Object Category', className: 'object-category'}, - { title: 'Departmental Request Total', className: 'request', isCost: true}, - { title: 'Departmental Request Notes', className: 'notes'}, - - // hidden columns used for calcs and info boxes - { title: 'Appropriation Name', className: 'approp-name', hide: true }, - { title: 'Cost Center Name', className: 'cc-name', hide: true }, - { title: 'Object Name', className: 'object-name', hide: true} -]; - -export function preparePageView(){ - // prepare page view - Body.reset(); - NavButtons.show(); - Sidebar.show(); - Table.adjustWidth('100%'); - - // update page text - Subtitle.update('Revenues'); - - // set up table - initializeRevTable() - - // enable continue button - NavButtons.Next.enable(); - - Prompt.Text.update('Review and edit revenue line items.'); - -} - -export async function initializeRevTable(){ - // load table data from storage - if(await Table.Data.load()) { - //after table is loaded, fill it - Table.show(); - Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, "Edit"); - // assign cost classes - Table.Columns.assignClasses(revenueColumns); - // enable editing - Table.Buttons.Edit.init(revRowOnEdit, Table.save); - // show info boxes on click - Tooltip.linkAll(); - } else { - Prompt.Text.update('No revenues for this fund.') - } -} - -function revRowOnEdit(){ - // make it editable - Table.Cell.createTextbox('request', true); - Table.Cell.createTextbox('notes'); - Table.Cell.createDropdown('recurring', ['One-Time', 'Recurring']); -} \ No newline at end of file diff --git a/src/js/views/archive/04_personnel/helpers.js b/src/js/views/archive/04_personnel/helpers.js deleted file mode 100644 index d63fb8d..0000000 --- a/src/js/views/archive/04_personnel/helpers.js +++ /dev/null @@ -1,146 +0,0 @@ - -import { FISCAL_YEAR } 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"; -import { AccountString, FundLookupTable, Services } from "../../utils/data_utils/budget_data_handlers.js"; -import { unformatCurrency } from "../../utils/common_utils.js"; - -export function preparePageView(){ - // prepare page view - Body.reset(); - NavButtons.show(); - Sidebar.show(); - Table.adjustWidth('90%'); - NavButtons.Next.enable(); - - // new row button - Table.Buttons.AddRow.updateText("Add new job"); - Table.Buttons.AddRow.show(); - - // update page text - Subtitle.update('Personnel'); - Prompt.Text.update(` - This table displays the number of FTEs in each job code for in your department's - current (amended) FY25 budget. To make edits to the number of positions, click the - "Edit" button on the row you would like to edit. The "Total Cost" column and the - summary sidebar will also update to reflect any edits. - `); -} - -function assignClasses() { - // record columns and their classes - const personnelColumns = [ - { title: 'Job Title', className: 'job-name' }, - { title: 'Account String', className: 'account-string' }, - { title: 'Service', className: 'service' }, - { title: `FY${FISCAL_YEAR} Requested FTE`, className: 'baseline-ftes' }, - { title: `FY${FISCAL_YEAR} Average Projected Salary/Wage`, className: 'avg-salary', isCost: true }, - { title: 'Total Cost', className: 'total-baseline', isCost: true }, - { title: 'Edit', className: 'edit' }, - // hidden columns needed for calculations - { title: 'Fringe Benefits Rate', className: 'fringe', hide: true }, - { title: 'Appropriation Name', className: 'approp-name', hide: true }, - { title: 'Appropriation', className: 'approp', hide: true }, - { title: 'Cost Center Name', className: 'cc-name', hide: true }, - { title: 'Cost Center', className: 'cc', hide: true }, - { title: 'General Increase Rate', className: 'general-increase-rate', hide: true}, - { title: 'Step/Merit Increase Rate', className: 'merit-increase-rate', hide: true}, - { title: `Average Salary/Wage as of 9/1/20${FISCAL_YEAR-2}`, className: 'current-salary', isCost: true, hide: true}, - ]; - - // assign cost classes - Table.Columns.assignClasses(personnelColumns) -} - -function personnelRowOnEdit(){ - Table.Cell.createTextbox('baseline-ftes'); - Table.Cell.createServiceDropdown(Services.list()); -} - -export async function initializePersonnelTable(){ - // load table data from local storage - if(await Table.Data.load()) { - //after table is loaded, show it - Table.show(); - Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, 'Edit'); - assignClasses(); - // add up the baseline costs and update sidebar - updateDisplayandTotals(); - // activate edit buttons - Table.Buttons.Edit.init(personnelRowOnEdit, updateDisplayandTotals); - } else { - Prompt.Text.update('No personnel expenditures for this fund.') - } -} - -// update sidebar and also cost totals when the FTEs are edited -function updateDisplayandTotals(){ - // calculate for each row - let rows = document.getElementsByTagName('tr'); - for (let i = 1; i < rows.length; i++){ - // fetch values for calculations - let avg_salary = Table.Cell.getValue(rows[i], 'avg-salary'); - let fringe = parseFloat(Table.Cell.getText(rows[i], 'fringe')); - let baseline_ftes = Table.Cell.getText(rows[i], 'baseline-ftes'); - - // calcuate #FTEs x average salary + COLA adjustments + merit adjustments + fringe - let total_baseline_cost = avg_salary * baseline_ftes * (1 + fringe); - - // update total column - Table.Cell.updateValue(rows[i], 'total-baseline', total_baseline_cost); - } - - // Save the table after all updates are done - Table.save(); - -} - -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 Title:', 'job-name', true); - Form.NewField.dropdown('Appropriation:', 'approp-name', FundLookupTable.getApprops(), true); - Form.NewField.dropdown('Cost Center:', 'cc-name', FundLookupTable.getCostCenters(), true); - Form.NewField.dropdown('Service', 'service', Services.list(), true); - Form.NewField.shortText('Number of FTEs requested:', 'baseline-ftes', true); - Form.NewField.shortText(`Projected average salary IN FISCAL YEAR ${FISCAL_YEAR}:`, 'avg-salary', true); - Form.NewField.shortText(`Expected fringe rate (as a percentage)`, 'fringe', true); - Form.SubmitButton.add(); - // Initialize form submission to table data - Modal.Submit.init(handleSubmitNewJob); -} - -function handleSubmitNewJob(event){ - // get answers from form, hide form, show answers in table - const responses = Form.fetchAllResponses(event); - // edit inputs from modal - responses['avg-salary'] = unformatCurrency(responses['avg-salary']); - responses['fringe'] = parseFloat(responses['fringe']) / 100; - responses['account-string'] = AccountString.build(responses['approp-name'], responses['cc-name']) - responses['approp'] = AccountString.getNumber(responses['approp-name']); - responses['cc'] = AccountString.getNumber(responses['cc-name']); - // 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.save(); - initializePersonnelTable(); - - } - -} diff --git a/src/js/views/archive/05_overtime/helpers.js b/src/js/views/archive/05_overtime/helpers.js deleted file mode 100644 index 82bc56b..0000000 --- a/src/js/views/archive/05_overtime/helpers.js +++ /dev/null @@ -1,156 +0,0 @@ - -import Prompt from '../../../components/prompt/prompt.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 Sidebar from '../../../components/sidebar/sidebar.js'; -import Table from '../../../components/table/table.js'; -import { AccountString, Services } from '../../../utils/data_utils/budget_data_handlers.js'; -import Modal from '../../../components/modal/modal.js'; -import Form from '../../../components/form/form.js'; -import { unformatCurrency } from '../../../utils/common_utils.js'; -import { FundLookupTable } from "../../../utils/data_utils/budget_data_handlers.js"; - - -export function preparePageView(){ - // prepare page view - Body.reset(); - NavButtons.show(); - Sidebar.show(); - - // enable next button - NavButtons.Next.enable(); - - // update page text - Subtitle.update('Overtime Estimates'); - - // activate table - initializeOTTable(); - Prompt.Text.update(`Please see your baseline overtime / holiday pay / shift premiums in the table below. - Make any edits and continue.`); - - // form for new row - setUpModal(); - setUpForm(); - - // show new row button - Table.Buttons.AddRow.updateText("Add new cost center"); - Table.Buttons.AddRow.show(); -} - -function setUpModal() { - // Initialize modal - Modal.clear(); - Modal.Link.add('add-btn'); - Modal.Title.update('New cost center for overtime'); -} - -function assignClasses() { - // record columns and their classes - const OT_cols = [ - // { title: 'Account String', className: 'account-string' }, - { title: `Cost Center Name`, className: 'cc-name' }, - { title: 'Appropriation Name', className: 'approp-name'}, - { title: 'Service', className: 'service' }, - { title: 'Recurring or One-Time', className: 'recurring'}, - { title: 'Hourly Employee Overtime (Wages)', className: 'OT-wages', isCost: true }, - { title: 'Salaried Employee Overtime (Salary)', className: 'OT-salary', isCost: true }, - { title: 'Total Cost (including benefits)', className : 'total', isCost: true}, - { title: 'Edit', className: 'edit'}, - // calc columns - { title: 'FICA Rate', className: 'fica', hide: true}, - { title: 'Account String', className: 'account-string', hide: true}, - { title: `Cost Center`, className: 'cc', hide: true }, - { title: 'Appropriation', className: 'approp', hide: true}, - ]; - - // assign cost classes - Table.Columns.assignClasses(OT_cols) -} - -function OTRowOnEdit(){ - Table.Cell.createTextbox('OT-wages', true); - Table.Cell.createTextbox('OT-salary', true); - Table.Cell.createServiceDropdown(Services.list()); - Table.Cell.createDropdown('recurring', ['One-Time', 'Recurring']); -} - -export async function initializeOTTable(){ - // load table data from local storage - if(await Table.Data.load()) { - //after table is loaded, fill it - Table.show(); - Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, 'Edit');; - assignClasses(); - // add up the baseline costs and update sidebar - updateDisplayandTotals(); - // activate edit buttons - Table.Buttons.Edit.init(OTRowOnEdit, updateDisplayandTotals); - } else { - Prompt.Text.update('No overtime expenditures for this fund.') - } -} - -function calculateTotalCost(salary, wages, fica_rate){ - fica_rate = parseFloat(fica_rate); - return (wages + salary) * (1 + fica_rate) ; -} - -// update sidebar and also cost totals when the FTEs are edited -function updateDisplayandTotals(){ - // calculate for each row - let rows = document.getElementsByTagName('tr'); - for (let i = 1; i < rows.length; i++){ - // fetch values for calculations - let OT_salary = Table.Cell.getValue(rows[i], 'OT-salary'); - let OT_wages = Table.Cell.getValue(rows[i], 'OT-wages'); - let fica_rate = Table.Cell.getText(rows[i], 'fica'); - - // add salary and wages and fringe benefits (FICA) - let row_total = calculateTotalCost(OT_salary, OT_wages, fica_rate); - - // update total - Table.Cell.updateValue(rows[i], 'total', row_total); - - //save data - Table.save(); - } -} - -export function setUpForm() { - // Set up form - Form.new('modal-body'); - Form.NewField.dropdown('Appropriation:', 'approp-name', FundLookupTable.getApprops(), true); - Form.NewField.dropdown('Cost Center:', 'cc-name', FundLookupTable.getCostCenters(), true); - Form.NewField.dropdown('Service', 'service', Services.list(), true); - Form.NewField.dropdown('Recurring or One-Time', 'recurring', ['Recurring', 'One-Time'], true); - Form.NewField.shortText('Overtime amount requested:', 'OT-wages', true); - Form.SubmitButton.add(); - // Initialize form submission to table data - Modal.Submit.init(handleSubmitNewRow); -} - -function handleSubmitNewRow(event){ - // get answers from form, hide form, show answers in table - const responses = Form.fetchAllResponses(event); - - // edit inputs from modal - responses['OT-wages'] = unformatCurrency(responses['OT-wages']); - responses['fica'] = 0.0765; - // create account string - responses['account-string'] = AccountString.build(responses['approp-name'], responses['cc-name']); - responses['approp'] = AccountString.getNumber(responses['approp-name']); - responses['cc'] = AccountString.getNumber(responses['cc-name']); - - // 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.save(); - initializeOTTable(); - - } - -} \ No newline at end of file diff --git a/src/js/views/archive/06_nonpersonnel/helpers.js b/src/js/views/archive/06_nonpersonnel/helpers.js deleted file mode 100644 index 21aee91..0000000 --- a/src/js/views/archive/06_nonpersonnel/helpers.js +++ /dev/null @@ -1,130 +0,0 @@ -import Prompt from "../../../components/prompt/prompt.js"; -import Sidebar from "../../../components/sidebar/sidebar.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 Subtitle from "../../../components/header/header.js"; -import Tooltip from "../../../components/tooltip/tooltip.js"; -import Modal from "../../../components/modal/modal.js"; -import Form from "../../../components/form/form.js"; -import { ObjectCategories, Services, AccountString } from "../../../utils/data_utils/budget_data_handlers.js"; -import { FundLookupTable } from "../../../utils/data_utils/budget_data_handlers.js"; -import { unformatCurrency } from "../../../utils/common_utils.js"; - -const nonPersonnelColumns = [ - { title: 'FY26 Request', className: 'request', isCost: true }, - { title: 'Service', className : 'service' }, - { title: 'Edit', className : 'edit' }, - { title : 'Account String', className : 'account-string'}, - { title: 'Recurring or One-Time', className: 'recurring'}, - - { title : 'CPA #', className : 'cpa'}, - - // hidden columns used for calcs and info boxes - { title: 'Appropriation Name', className: 'approp-name', hide: true }, - { title: 'Cost Center Name', className: 'cc-name', hide: true }, - { title: 'Appropriation', className: 'approp', hide: true }, - { title: 'Cost Center', className: 'cc', hide: true }, - { title: 'Contract End Date', className: 'contract-end', hide: true}, - { title: 'Amount Remaining on Contract', className: 'remaining', isCost: true , hide: true}, - { title: 'Object Name', className: 'object-name', hide: true}, - { title: 'Object', className: 'object', hide: true}, - { title: 'Vendor Name', className: 'vendor', hide: true}, - { title: 'Object Category', className: 'object-category', hide: true}, - { title: 'BPA/CPA Description', className: 'cpa-description', hide: true} -]; - -export function preparePageView(){ - // prepare page view - Body.reset(); - NavButtons.show(); - Sidebar.show(); - Table.adjustWidth('100%'); - // update page text - Subtitle.update('Non-Personnel'); - Prompt.Text.update('Review and edit non-personnel line items.'); - NavButtons.Next.enable(); - - // form for new row - setUpModal(); - setUpForm(); - - // show new row button - Table.Buttons.AddRow.updateText("Add new non-personnel item"); - Table.Buttons.AddRow.show() -} - -export async function initializeNonpersonnelTable(){ - // load table data from storage - if(await Table.Data.load()) { - //after table is loaded, fill it - Table.show(); - Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, "Edit"); - // assign cost classes - Table.Columns.assignClasses(nonPersonnelColumns); - // enable editing - Table.Buttons.Edit.init(nonPersonnelRowOnEdit, Table.save); - // show detail buttons - Tooltip.linkAll(); - } else { - Prompt.Text.update('No non-personnel expenditures for this fund.') - } -} - -function nonPersonnelRowOnEdit(){ - // make it editable - Table.Cell.createTextbox('request', true); - Table.Cell.createServiceDropdown(); - Table.Cell.createDropdown('recurring', ['One-Time', 'Recurring']); -} - -export function setUpModal() { - // Initialize modal - Modal.clear(); - Modal.Link.add('add-btn'); - Modal.Title.update('New non-personnel item'); -} - -export function setUpForm() { - // Set up form - Form.new('modal-body'); - Form.NewField.dropdown('Appropriation:', 'approp-name', FundLookupTable.getApprops(), true); - Form.NewField.dropdown('Cost Center:', 'cc-name', FundLookupTable.getCostCenters(), true); - Form.NewField.dropdown('Object Category:', 'object-category', ObjectCategories.list, true); - // TODO: maybe give dropdown based on selected obj category - Form.NewField.shortText('Object Number (if known):', 'object', false); - Form.NewField.dropdown('Service', 'service', Services.list(), true); - Form.NewField.longText('Describe your new request:', 'description', true); - Form.NewField.dropdown('Recurring or One-Time', 'recurring', ['Recurring', 'One-Time'], true); - Form.NewField.shortText('Amount requested:', 'request', true); - Form.SubmitButton.add(); - // Initialize form submission to table data - Modal.Submit.init(submitNewRow); -} - - -function submitNewRow(event){ - // get answers from form, hide form, show answers in table - const responses = Form.fetchAllResponses(event); - // edit inputs from modal - responses['avg-salary'] = unformatCurrency(responses['avg-salary']); - responses['fringe'] = parseFloat(responses['fringe']) / 100; - // create account string - responses['account-string'] = AccountString.build(responses['approp-name'], responses['cc-name'], responses['object']); - responses['approp'] = AccountString.getNumber(responses['approp-name']); - responses['cc'] = AccountString.getNumber(responses['cc-name']); - // TODO: build out lookup table from meta.obj tab from detail sheet? - responses['object-name'] = responses['object']; - - // 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.save(); - initializeNonpersonnelTable(); - - } - -} diff --git a/src/js/views/archive/07_new_initiatives/helpers.js b/src/js/views/archive/07_new_initiatives/helpers.js deleted file mode 100644 index 1ef9477..0000000 --- a/src/js/views/archive/07_new_initiatives/helpers.js +++ /dev/null @@ -1,169 +0,0 @@ - -import Prompt from '../../../components/prompt/prompt.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 '../../view_logic.js' -import Subtitle from '../../../components/header/header.js' -import Sidebar from '../../../components/sidebar/sidebar.js' -import { FundLookupTable, AccountString } from '../../../utils/data_utils/budget_data_handlers.js' - -const explanation = `New initiative submissions will count as supplemental line items and will be the starting - point for a conversation with both OB and ODFS, who will help with the details.` - -const dropdownOptions = ['N/A', 'One-Time', 'Recurring'] - -const initiativesCols = [ - { title: 'Initiative Name', className: 'init-name' }, - { title: 'Account String', className: 'account-string' }, - { title: 'Ballpark Total Expenses', className: 'total', isCost: true }, - { title: 'Personnel Cost', className: 'personnel', isCost: true }, - { title: 'Non-personnel Cost', className: 'nonpersonnel', isCost: true }, - { title: 'Revenue', className: 'revenue', isCost: true }, - { title: 'Revenue Type', className: 'rev-type' }, - { title: 'Edit', className : 'edit' }, - - // hide the explanation columns - { title: 'Q1', className: 'q1', hide: true }, - { title: 'Q2', className: 'q2', hide: true }, - { title: 'Q3', className: 'q3', hide: true }, - - // hide the account string breakdown columns too - { title: 'Appropriation Name', className: 'approp-name', hide: true }, - { title: 'Cost Center Name', className: 'cc-name', hide: true }, - { title: 'Appropriation', className: 'approp', hide: true }, - { title: 'Cost Center', className: 'cc', hide: true }, - { title: 'Fund Name', className: 'fund-name', hide: true }, - { title: 'Fund', className: 'fund', hide: true } -]; - -export function initializePageView() { - // Prepare page view - Body.reset(); - NavButtons.show(); - Sidebar.show(); - NavButtons.Next.enable(); - - //table appearance - Table.adjustWidth('100%'); - Table.Buttons.AddRow.updateText('Add new initiative'); - - // remove fund selection - localStorage.setItem("fund", ''); - - // Load text - Subtitle.update('New Initiatives'); - Prompt.Text.update('This is the place to propose new initiatives for FY26. ' + explanation); - NavButtons.Next.enable - // Prompt.Buttons.Left.updateText('Yes, propose a new initiative'); - // Prompt.Buttons.Right.updateText('No new initiatives'); - // clicking 'no new initialitives' will also take us to the next page - Table.Buttons.AddRow.show(); - // Prompt.Buttons.Right.addAction(nextPage); - // Prompt.Buttons.Left.addAction(NavButtons.Next.enable); -} - -export function setUpModal() { - // Initialize modal - Modal.clear(); - Modal.Link.add('option1'); - Modal.Title.update('New initiative'); - Modal.Link.add('add-btn'); -} - -export function setUpForm() { - // Set up form - Form.new('modal-body'); - - // general questions - Form.NewField.shortText('Initiative Name:', 'init-name', true); - Form.NewField.longText('What is the business case for the Initiative?', 'q1', true); - Form.NewField.longText(`Why is the initiative needed? What is the value-add to residents? - What is the Department’s plan for implementing the Initiative?`, 'q2', true); - Form.NewField.longText(`Why can’t the Initiative be funded with the Department’s baseline budget?`, 'q3', true); - - // TODO: Edit to drop down - Form.NewField.dropdown('Fund:', 'fund-name', FundLookupTable.listFundNames(), true); - Form.NewField.dropdown('Appropriation (if known):', 'approp-name', FundLookupTable.getApprops(), true); - Form.NewField.dropdown('Cost Center (if known):', 'cc-name', FundLookupTable.getCostCenters(), true); - - // Numbers - Form.NewField.numericInput('What is your ballpark estimate of TOTAL ADDITONAL expenses associated with this initiative?', - 'total', false); - Form.NewField.numericInput('Estimate of ADDITONAL personnel cost?', 'personnel', false); - Form.NewField.numericInput('Estimate of ADDITONAL nonpersonnel cost?', 'nonpersonnel', false); - Form.NewField.numericInput('Estimate of ADDITONAL revenue (if applicable)?', 'revenue', false); - Form.NewField.dropdown(`If there will be revenue, is it one-time or recurring?`, - 'rev-type', dropdownOptions); - - - Form.SubmitButton.add(); - // Initialize form submission to table data - Modal.Submit.init(submitNewRow); -} - -function assignClasses() { - // assign cost classes - Table.Columns.assignClasses(initiativesCols) -} - -export async function initializeInitTable(){ - - // load table data from storage - if(await Table.Data.load()) { - // after table is loaded, fill it - Table.Columns.addAtEnd(Table.Buttons.edit_confirm_btns, "Edit"); - assignClasses(); - // show table - Table.show(); - // enable editing - Table.Buttons.Edit.init(rowOnEdit, Table.save); - } else { - Table.clear(); - console.log('no data'); - } -} - -function rowOnEdit(){ - Table.Cell.createTextbox('total', true); - Table.Cell.createTextbox('revenue', true); - Table.Cell.createTextbox('personnel', true); - Table.Cell.createTextbox('nonpersonnel', true); - Table.Cell.createTextbox('init-name'); - Table.Cell.createDropdown('rev-type', dropdownOptions); -} - -function submitNewRow(event){ - // get answers from form, hide form, show answers in table - const responses = Form.fetchAllResponses(event); - console.log(responses); - - // create account string columns - responses['approp'] = AccountString.getNumber(responses['approp-name']); - responses['cc'] = AccountString.getNumber(responses['cc-name']); - responses['fund'] = AccountString.getNumber(responses['fund-name']); - responses['account-string'] = AccountString.build(responses['approp-name'], responses['cc-name'], null, responses['fund']); - - // make sure it's not an empty response - if (Object.values(responses)[0] != ''){ - Modal.hide(); - // add data to table - Table.Rows.add(responses, initiativesCols); - Table.save(); - initializeInitTable(); - Table.Buttons.AddRow.updateText('Add another new initiative'); - } -} - -export function removeModalLinks(){ - Modal.Link.remove('option1'); - Modal.Link.remove('add-btn'); -} - -export function removePromptButtonListeners(){ - Prompt.Buttons.Right.removeAction(nextPage); - Prompt.Buttons.Left.removeAction(NavButtons.Next.enable); - Modal.clear(); -} \ No newline at end of file diff --git a/src/js/views/archive/08_summary/helpers.js b/src/js/views/archive/08_summary/helpers.js deleted file mode 100644 index 4c07102..0000000 --- a/src/js/views/archive/08_summary/helpers.js +++ /dev/null @@ -1,55 +0,0 @@ - -import Prompt from '../../components/prompt/prompt.js' -import Body from "../../components/body/body.js"; -import Subtitle from "../../components/header/header.js"; -import { visitPage } from "../view_logic.js"; -import { Accordion } from "../../components/accordion/accordion.js"; -import { downloadXLSX } from "../../utils/data_utils/XLSX_handlers.js"; -import { Baseline, CurrentFund } from '../../utils/data_utils/local_storage_handlers.js'; -import { TARGET } from '../../init.js'; -import { formatCurrency } from '../../utils/common_utils.js'; - -export function summaryView(){ - - // show/hide elements - Body.reset(); - Accordion.build(); - Accordion.show(); - - // set fund to none - CurrentFund.reset(); - - // prompt buttons - Prompt.Buttons.Right.updateText('Download Excel'); - Prompt.Buttons.Left.updateText('Start over with new Excel upload'); - // add button links - Prompt.Buttons.Left.addAction(returnToWelcome); - Prompt.Buttons.Right.addAction(downloadXLSX); - - // update page text - Subtitle.update('Summary'); - compareToTarget() -} - -function compareToTarget(){ - const baseline = new Baseline; - if (baseline.total() <= TARGET){ - Prompt.Text.update(`Congrats! Your budget is below your target! - Edit any line items below or download your completed Excel.`); - Prompt.show(); - } else { - Prompt.Text.update(`Your budget is above your target of ${formatCurrency(TARGET)}. - Please expand the summary table below and edit line items until you meet your target. - When you meet the target, you will be able to download the Excel sheet.`); - Prompt.Buttons.Right.disable(); - Prompt.show(); - } -} - -const returnToWelcome = () => {visitPage('welcome')} - -export function disablePromptButtons(){ - Prompt.Buttons.Left.removeAction(returnToWelcome); - Prompt.Buttons.Right.removeAction(downloadXLSX); - Prompt.Buttons.Right.enable(); -} \ No newline at end of file From 79f4b238ef85356ecf450bbbeb1329356ea8c690 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 29 Jul 2024 13:39:39 -0400 Subject: [PATCH 16/22] move and separate JS classes --- src/js/models/baseline.js | 43 +++++++++++++++++++++++++ src/js/models/current_fund.js | 19 +++++++++++ src/js/models/current_page.js | 16 ++++++++++ src/js/models/fund.js | 60 +++++++++++++++++++++++++++++++++++ src/js/models/initiative.js | 31 ++++++++++++++++++ src/js/models/supplemental | 34 ++++++++++++++++++++ 6 files changed, 203 insertions(+) create mode 100644 src/js/models/baseline.js create mode 100644 src/js/models/current_fund.js create mode 100644 src/js/models/current_page.js create mode 100644 src/js/models/fund.js create mode 100644 src/js/models/initiative.js create mode 100644 src/js/models/supplemental diff --git a/src/js/models/baseline.js b/src/js/models/baseline.js new file mode 100644 index 0000000..f75911c --- /dev/null +++ b/src/js/models/baseline.js @@ -0,0 +1,43 @@ +import Fund from "./fund.js"; + +export class Baseline { + // baseline will just contain a list of funds, each with + // running tallies for their budgets + constructor() { + const allFunds = FundLookupTable.listFunds(); + this.funds = []; + allFunds.forEach((fund) => { + this.funds.push(new Fund(fund)); + }); + } + + personnel() { + let total = 0; + this.funds.forEach(fund => { + total += fund.getPersonnelCost(); + }); + return total; + } + + nonpersonnel() { + let total = 0; + this.funds.forEach(fund => { + total += fund.getNonPersonnelCost(); + }); + return total; + } + + revenue() { + let total = 0; + this.funds.forEach(fund => { + total += fund.getRevenue(); + }); + return total; + } + + total() { + return this.nonpersonnel() + this.personnel() - this.revenue(); + } +} + +export default Baseline; \ No newline at end of file diff --git a/src/js/models/current_fund.js b/src/js/models/current_fund.js new file mode 100644 index 0000000..21786f4 --- /dev/null +++ b/src/js/models/current_fund.js @@ -0,0 +1,19 @@ + +import { FundLookupTable } from "../utils/data_utils/budget_data_handlers"; + +export const CurrentFund = { + update : function(fund){ + localStorage.setItem('fund', fund); + }, + number : function(){ + return localStorage.getItem("fund"); + }, + name : function(){ + return FundLookupTable.getName( this.number()); + }, + reset : function() { + this.update(''); + } +} + +export default CurrentFund; \ No newline at end of file diff --git a/src/js/models/current_page.js b/src/js/models/current_page.js new file mode 100644 index 0000000..b3a5144 --- /dev/null +++ b/src/js/models/current_page.js @@ -0,0 +1,16 @@ +import { visitPage } from "../views/view_logic"; + +export const CurrentPage = { + update : function(page){ + localStorage.setItem('page_state', page); + }, + load : function(){ + const pageState = localStorage.getItem('page_state'); + return pageState !== null ? pageState : 'welcome'; + }, + visit : function(){ + visitPage(this.load()); + } +} + +export default CurrentPage; \ No newline at end of file diff --git a/src/js/models/fund.js b/src/js/models/fund.js new file mode 100644 index 0000000..59268c2 --- /dev/null +++ b/src/js/models/fund.js @@ -0,0 +1,60 @@ + +import { colSum } from "../utils/common_utils"; + +// Class to hold information on a specific fund and table +class StoredTable { + constructor(page, fund){ + this.name = `${page}_${fund}`; + this.page = page; + this.table = loadTableData(this.name); + } + + totalCol() { + switch(this.page){ + case 'personnel': + return 'Total Cost'; + case 'overtime': + return 'Total Cost (including benefits)'; + case 'nonpersonnel': + return `FY${FISCAL_YEAR} Request`; + case 'revenue': + return `Departmental Request Total`; + default: + break; + } + } + getSum() { + // fill with zero until there is something saved in storage + return colSum(this.table, this.totalCol(), this.name); + } + +} + +// Holds all the detailed data for one fund's budget +export class Fund { + constructor(fund){ + this.fund = fund; + this.personnel = new StoredTable('personnel', fund); + this.overtime = new StoredTable('overtime', fund); + this.nonpersonnel = new StoredTable('nonpersonnel', fund); + this.revenue = new StoredTable('revenue', fund); + } + + getPersonnelCost() { + return this.personnel.getSum() + this.overtime.getSum(); + } + + getNonPersonnelCost() { + return this.nonpersonnel.getSum(); + } + + getRevenue() { + return this.revenue.getSum(); + } + + getTotal() { + return this.getNonPersonnelCost() + this.getPersonnelCost() - this.getRevenue() + } +} + +export default Fund; \ No newline at end of file diff --git a/src/js/models/initiative.js b/src/js/models/initiative.js new file mode 100644 index 0000000..1d5e3c0 --- /dev/null +++ b/src/js/models/initiative.js @@ -0,0 +1,31 @@ + +// data structure to hold information on new initiatives + +export class Initiative { + + constructor(row) { + this.data = row; + this.name = row['Initiative Name']; + } + + expenses() { + if (this.data['Ballpark Total Expenses']) { + return this.data['Ballpark Total Expenses']; + } else { + return 0; + } + } + + revenue() { + if (this.data['Revenue']) { + return this.data['Revenue']; + } else { + return 0; + } + } + + net() { return this.expenses() - this.revenue() } + +} + +export default Initiative; \ No newline at end of file diff --git a/src/js/models/supplemental b/src/js/models/supplemental new file mode 100644 index 0000000..1cfb9ca --- /dev/null +++ b/src/js/models/supplemental @@ -0,0 +1,34 @@ + +import Initiative from "./initiative.js"; + +// data structure to hold supplemental requests +export class Supplemental { + constructor() { + this.table = loadTableData('new-inits'); + this.initiatives = []; + if(this.table){ + this.table.forEach((row) => { + this.initiatives.push(new Initiative(row)); + }); + } + } + + getInits() { + return this.table.map((item) => { return item['Initiative Name'] }); + } + + expenses() { + return colSum(this.table, 'Ballpark Total Expenses'); + } + + revenue() { + return colSum(this.table, 'Revenue'); + } + + total(){ + return this.expenses() - this.revenue(); + } + +} + +export default Supplemental; \ No newline at end of file From d97d4e4502223b2d45a00507e50ba7348a31c836 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 29 Jul 2024 13:45:06 -0400 Subject: [PATCH 17/22] resolve imports --- src/js/components/header/header.js | 2 +- src/js/components/sidebar/sidebar.js | 3 +- src/js/components/table/subcomponents/data.js | 22 +- src/js/components/table/table.js | 31 ++- src/js/init.js | 2 +- src/js/models/baseline.js | 2 + src/js/models/current_fund.js | 2 +- src/js/models/fund_lookup_table.js | 113 ++++++++ .../models/{supplemental => supplemental.js} | 0 src/js/utils/common_utils.js | 28 +- .../utils/data_utils/budget_data_handlers.js | 114 -------- .../data_utils/local_storage_handlers.js | 257 ++---------------- src/js/views/view_class.js | 2 +- src/js/views/view_logic.js | 3 +- 14 files changed, 202 insertions(+), 379 deletions(-) create mode 100644 src/js/models/fund_lookup_table.js rename src/js/models/{supplemental => supplemental.js} (100%) diff --git a/src/js/components/header/header.js b/src/js/components/header/header.js index d3f7b71..53ab0de 100644 --- a/src/js/components/header/header.js +++ b/src/js/components/header/header.js @@ -1,6 +1,6 @@ import './header.css'; -import { CurrentFund } from "../../utils/data_utils/local_storage_handlers.js"; +import CurrentFund from '../../models/current_fund'; export const Subtitle = { update : function(subtitle){ diff --git a/src/js/components/sidebar/sidebar.js b/src/js/components/sidebar/sidebar.js index 2b06b33..a867a78 100644 --- a/src/js/components/sidebar/sidebar.js +++ b/src/js/components/sidebar/sidebar.js @@ -2,7 +2,8 @@ import './sidebar.css' import { formatCurrency } from "../../utils/common_utils.js"; import { TARGET } from "../../init.js"; -import { Baseline, Supplemental } from "../../utils/data_utils/local_storage_handlers.js"; +import Baseline from '../../models/baseline.js' +import Supplemental from '../../models/supplemental.js' // Assuming you have a CSS variable --main-color defined on the :root const root = document.documentElement; diff --git a/src/js/components/table/subcomponents/data.js b/src/js/components/table/subcomponents/data.js index 67c4b23..16f2188 100644 --- a/src/js/components/table/subcomponents/data.js +++ b/src/js/components/table/subcomponents/data.js @@ -1,5 +1,6 @@ import { FundLookupTable } from "../../../utils/data_utils/budget_data_handlers.js"; -import { CurrentFund, CurrentPage, loadTableData, saveTableData } from "../../../utils/data_utils/local_storage_handlers.js"; +import CurrentFund from '../../../models/current_fund.js' +import CurrentPage from '../../../models/current_page.js' function fillTable(data) { try { @@ -33,24 +34,25 @@ function fillTable(data) { } catch(error) { console.error('No table saved in localStorage:', error); } - saveTableData(); } async function loadFromStorage(){ - // look up table in storage and pass to table load function\ + // look up table name in storage if (CurrentFund.number()){ var key = `${CurrentPage.load()}_${CurrentFund.number()}`; } else { var key = CurrentPage.load(); } - const data = await loadTableData(key); - if (!data){ - // if no table in storage, return 0 + // load from local storage + const data = localStorage.getItem(key); + + // if nothing in storage, return a zero + if ( data == '' || data == '[]' ) { return 0; - } else { - fillTable(data); - return 1; - } + }; + // otherwise, fill table in HTML and return success (1) + fillTable(await JSON.parse(data)); + return 1; } diff --git a/src/js/components/table/table.js b/src/js/components/table/table.js index 94f29dd..9986323 100644 --- a/src/js/components/table/table.js +++ b/src/js/components/table/table.js @@ -6,8 +6,11 @@ import Columns from './subcomponents/columns.js' import Header from './subcomponents/headers.js' import Rows from './subcomponents/rows.js' import Data from './subcomponents/data.js' -import { saveTableData } from '../../utils/data_utils/local_storage_handlers.js' import Tooltip from '../tooltip/tooltip.js'; +import { convertToJSON } from "../../utils/data_utils/JSON_data_handlers.js"; +import Sidebar from '../sidebar/sidebar.js'; +import CurrentFund from '../../models/current_fund.js'; +import CurrentPage from '../../models/current_page.js'; function adjustTableWidth(width_pct){ const table = document.getElementById('main-table'); @@ -31,6 +34,24 @@ function hideTable(){ Buttons.AddRow.hide(); } +function saveTableData() { + // remove the detail text + Tooltip.unlink(); + // get table + var table = document.getElementById('main-table'); + // determine save_as name + if (CurrentFund.number()) { + var save_as = `${CurrentPage.load()}_${CurrentFund.number()}`; + } else { + var save_as = CurrentPage.load(); + } + localStorage.setItem(save_as, convertToJSON(table, ['Edit'])); + // update sidebar with new data + Sidebar.updateTotals(); + // relink, depending on page + Tooltip.linkAll(); +} + const Table = { Buttons : Buttons, Cell : Cell, @@ -45,13 +66,7 @@ const Table = { clear : clearTable, hide : hideTable, show : showTable, - save : async function() { - // remove the detail text - Tooltip.unlink(); - saveTableData(); - // relink, depending on page - Tooltip.linkAll(); - } + save : saveTableData } export default Table; \ No newline at end of file diff --git a/src/js/init.js b/src/js/init.js index 8033d91..e079cef 100644 --- a/src/js/init.js +++ b/src/js/init.js @@ -7,7 +7,7 @@ export let TARGET = 10000000; export var FISCAL_YEAR = '26'; // import functions -import { CurrentPage } from './utils/data_utils/local_storage_handlers.js'; +import CurrentPage from './models/current_page.js'; // sheets to expect on detail sheet export const SHEETS = { diff --git a/src/js/models/baseline.js b/src/js/models/baseline.js index f75911c..4532899 100644 --- a/src/js/models/baseline.js +++ b/src/js/models/baseline.js @@ -1,4 +1,6 @@ import Fund from "./fund.js"; +import FundLookupTable from "./fund_lookup_table.js"; + export class Baseline { // baseline will just contain a list of funds, each with diff --git a/src/js/models/current_fund.js b/src/js/models/current_fund.js index 21786f4..b040e90 100644 --- a/src/js/models/current_fund.js +++ b/src/js/models/current_fund.js @@ -1,5 +1,5 @@ -import { FundLookupTable } from "../utils/data_utils/budget_data_handlers"; +import FundLookupTable from "./fund_lookup_table"; export const CurrentFund = { update : function(fund){ diff --git a/src/js/models/fund_lookup_table.js b/src/js/models/fund_lookup_table.js new file mode 100644 index 0000000..37b90dc --- /dev/null +++ b/src/js/models/fund_lookup_table.js @@ -0,0 +1,113 @@ +import CurrentFund from "./current_fund.js"; +import { getUniqueValues } from "../utils/common_utils.js"; + +export const FundLookupTable = { + retrieve : function() { + return JSON.parse(localStorage.getItem('fund-lookup-table')) || {}; + }, + save : function(fundDict){ + localStorage.setItem('fund-lookup-table', JSON.stringify(fundDict)); + }, + + update : function(fundData){ + const table = this.retrieve(); + + for (let fund of Object.keys(fundData)){ + + // add to lookup table if not in there already + if (!table[fund]){ + // get fund name + const fundName = fundData[fund][0]['Fund Name']; + // add fund to dictionary + table[fund] = {}; + table[fund]['name'] = fundName; + table[fund]['viewed'] = false; + // build lists of unique cost centers and appropriations + table[fund]['approp'] = getUniqueValues(fundData[fund], 'Appropriation Name'); + table[fund]['cc'] = getUniqueValues(fundData[fund], 'Cost Center Name'); + } + } + // save any updates + this.save(table); + }, + + getAll: function(key) { + // function to aggregate all approps or CCs for every fund in one array + const funds = this.retrieve(); + const ret = []; + for (const fund in funds) { + if (funds.hasOwnProperty(fund)) { + for (let i in funds[fund][key]){ + ret.push(funds[fund][key][i]); + } + } + } + return ret; + }, + + getCostCenters : function() { + // get current fund + const fund = CurrentFund.number() + if (this.retrieve()[fund]){ + return this.retrieve()[fund]['cc']; + } + // if no fund (ie. we're on the new initiative page), return all options + return this.getAll('cc'); + }, + + getApprops : function() { + // get current fund + const fund = CurrentFund.number() + if (this.retrieve()[fund]){ + return this.retrieve()[fund]['approp']; + } + // if no fund (ie. we're on the new initiative page), return all options + return this.getAll('approp'); + }, + + reset : function() { + this.save({}); + }, + getName : function(number){ + if(!number || !this.retrieve()) { return '' }; + return this.retrieve()[number]['name']; + }, + listFunds : function(){ + return Object.keys(this.retrieve()); + }, + listFundNames : function(){ + const funds = this.retrieve(); + // initialize array + var ret = []; + Object.keys(funds).forEach( (fund_number) => { + var fund_name = funds[fund_number]['name']; + ret.push(fund_name); + }); + return ret; + }, + editFund : function(fund){ + const table = this.retrieve(); + if (table[fund]){ + table[fund]['viewed'] = true; + this.save(table); + } else { + console.error('No fund selected.'); + } + + }, + listUneditedFunds : function(){ + const table = this.retrieve(); + const ret = []; + this.listFunds().forEach(key => { + if (!table[key]['viewed']){ + ret.push(key); + } + }); + return ret; + }, + fundsLeft : function(){ + return (this.listUneditedFunds().length > 0); + } +} + +export default FundLookupTable \ No newline at end of file diff --git a/src/js/models/supplemental b/src/js/models/supplemental.js similarity index 100% rename from src/js/models/supplemental rename to src/js/models/supplemental.js diff --git a/src/js/utils/common_utils.js b/src/js/utils/common_utils.js index 814fadf..b32552c 100644 --- a/src/js/utils/common_utils.js +++ b/src/js/utils/common_utils.js @@ -44,4 +44,30 @@ export function removeNewLines(str){ str = str.replaceAll(' ', ' '); str = str.replace(/^\s+|\s+$/g, ''); return str; -} \ No newline at end of file +} + +export function colSum(table, colName) { + // fill with zero until there is something saved in storage + if(!table || table == ''){ + return 0; + } + const headers = Object.keys(table[0]); + if (headers.includes(colName)) { + let sum = 0; + for (let i = 0; i < table.length; i++){ + var value = Math.round(parseFloat(table[i][colName])); + // treat NaN (non-numerics) as zeroes + if (value) { sum += value; } + } + return sum; + } else { + // console.error(`Could not find expected total column in saved data for ${name}. Returning 0. See StoredTable.totalCol() switch.`); + return 0; + } + +} + +export function getUniqueValues(data, key) { + const values = data.map(obj => obj[key]); + return Array.from(new Set(values)); +} diff --git a/src/js/utils/data_utils/budget_data_handlers.js b/src/js/utils/data_utils/budget_data_handlers.js index 187a046..6b3b64e 100644 --- a/src/js/utils/data_utils/budget_data_handlers.js +++ b/src/js/utils/data_utils/budget_data_handlers.js @@ -1,118 +1,4 @@ -import { CurrentFund } from "./local_storage_handlers"; -function getUniqueValues(data, key) { - const values = data.map(obj => obj[key]); - return Array.from(new Set(values)); -} - -export const FundLookupTable = { - retrieve : function() { - return JSON.parse(localStorage.getItem('fund-lookup-table')) || {}; - }, - save : function(fundDict){ - localStorage.setItem('fund-lookup-table', JSON.stringify(fundDict)); - }, - - update : function(fundData){ - const table = this.retrieve(); - - for (let fund of Object.keys(fundData)){ - - // add to lookup table if not in there already - if (!table[fund]){ - // get fund name - const fundName = fundData[fund][0]['Fund Name']; - // add fund to dictionary - table[fund] = {}; - table[fund]['name'] = fundName; - table[fund]['viewed'] = false; - // build lists of unique cost centers and appropriations - table[fund]['approp'] = getUniqueValues(fundData[fund], 'Appropriation Name'); - table[fund]['cc'] = getUniqueValues(fundData[fund], 'Cost Center Name'); - } - } - // save any updates - this.save(table); - }, - - getAll: function(key) { - // function to aggregate all approps or CCs for every fund in one array - const funds = this.retrieve(); - const ret = []; - for (const fund in funds) { - if (funds.hasOwnProperty(fund)) { - for (let i in funds[fund][key]){ - ret.push(funds[fund][key][i]); - } - } - } - return ret; - }, - - getCostCenters : function() { - // get current fund - const fund = CurrentFund.number() - if (this.retrieve()[fund]){ - return this.retrieve()[fund]['cc']; - } - // if no fund (ie. we're on the new initiative page), return all options - return this.getAll('cc'); - }, - - getApprops : function() { - // get current fund - const fund = CurrentFund.number() - if (this.retrieve()[fund]){ - return this.retrieve()[fund]['approp']; - } - // if no fund (ie. we're on the new initiative page), return all options - return this.getAll('approp'); - }, - - reset : function() { - this.save({}); - }, - getName : function(number){ - if(!number || !this.retrieve()) { return '' }; - return this.retrieve()[number]['name']; - }, - listFunds : function(){ - return Object.keys(this.retrieve()); - }, - listFundNames : function(){ - const funds = this.retrieve(); - // initialize array - var ret = []; - Object.keys(funds).forEach( (fund_number) => { - var fund_name = funds[fund_number]['name']; - ret.push(fund_name); - }); - return ret; - }, - editFund : function(fund){ - const table = this.retrieve(); - if (table[fund]){ - table[fund]['viewed'] = true; - this.save(table); - } else { - console.error('No fund selected.'); - } - - }, - listUneditedFunds : function(){ - const table = this.retrieve(); - const ret = []; - this.listFunds().forEach(key => { - if (!table[key]['viewed']){ - ret.push(key); - } - }); - return ret; - }, - fundsLeft : function(){ - return (this.listUneditedFunds().length > 0); - } -} // data structure to save the possible service options for the department export const Services = { diff --git a/src/js/utils/data_utils/local_storage_handlers.js b/src/js/utils/data_utils/local_storage_handlers.js index e310325..644a197 100644 --- a/src/js/utils/data_utils/local_storage_handlers.js +++ b/src/js/utils/data_utils/local_storage_handlers.js @@ -1,241 +1,18 @@ -import { FISCAL_YEAR } from "../../init.js"; -import Sidebar from "../../components/sidebar/sidebar.js"; -import { initializePages, visitPage } from "../../views/view_logic.js"; -import { fetchJSON } from "./JSON_data_handlers.js"; -import { FundLookupTable } from "./budget_data_handlers.js"; -import { convertToJSON } from "./JSON_data_handlers.js"; +// import { initializePages } from "../../views/view_logic.js"; +// import { fetchJSON } from "./JSON_data_handlers.js"; + +// function deleteTable(name){ +// localStorage.setItem(name, ''); +// } + +// export async function deleteAllTables(){ +// var funds = await fetchJSON(DATA_ROOT + 'funds.json'); +// funds = funds.map((item) => { return item.Name }); +// for (const page in initializePages()){ +// for(const i in funds){ +// deleteTable(`${page}_${funds[i]}`); +// } +// } +// deleteTable('new-inits'); +// } -export const CurrentPage = { - update : function(page){ - localStorage.setItem('page_state', page); - }, - load : function(){ - const pageState = localStorage.getItem('page_state'); - return pageState !== null ? pageState : 'welcome'; - }, - visit : function(){ - visitPage(this.load()); - } -} - -export const CurrentFund = { - update : function(fund){ - localStorage.setItem('fund', fund); - }, - number : function(){ - return localStorage.getItem("fund"); - }, - name : function(){ - return FundLookupTable.getName( this.number()); - }, - reset : function() { - this.update(''); - } -} - -// TODO: consider moving this into a const for Current Table (or to the table component) -export function saveTableData() { - var table = document.getElementById('main-table'); - if (CurrentFund.number()) { - var save_as = `${CurrentPage.load()}_${CurrentFund.number()}`; - } else { - var save_as = CurrentPage.load(); - } - localStorage.setItem(save_as, convertToJSON(table, ['Edit'])); - console.log('saved'); - Sidebar.updateTotals(); -} - -function deleteTable(name){ - localStorage.setItem(name, ''); -} - -export async function deleteAllTables(){ - var funds = await fetchJSON(DATA_ROOT + 'funds.json'); - funds = funds.map((item) => { return item.Name }); - for (const page in initializePages()){ - for(const i in funds){ - deleteTable(`${page}_${funds[i]}`); - } - } - deleteTable('new-inits'); -} - -export function loadTableData(name){ - const data = localStorage.getItem(name); - if ( data == '' || data == '[]' ) { - return 0; - }; - return JSON.parse(data); -} - -// Class to hold information on a specific fund and table -class StoredTable { - constructor(page, fund){ - this.name = `${page}_${fund}`; - this.page = page; - this.table = loadTableData(this.name); - } - - totalCol() { - switch(this.page){ - case 'personnel': - return 'Total Cost'; - case 'overtime': - return 'Total Cost (including benefits)'; - case 'nonpersonnel': - return `FY${FISCAL_YEAR} Request`; - case 'revenue': - return `Departmental Request Total`; - default: - break; - } - } - getSum() { - // fill with zero until there is something saved in storage - return colSum(this.table, this.totalCol(), this.name); - } - -} - -function colSum(table, colName) { - // fill with zero until there is something saved in storage - if(!table || table == ''){ - return 0; - } - const headers = Object.keys(table[0]); - if (headers.includes(colName)) { - let sum = 0; - for (let i = 0; i < table.length; i++){ - var value = Math.round(parseFloat(table[i][colName])); - // treat NaN (non-numerics) as zeroes - if (value) { sum += value; } - } - return sum; - } else { - // console.error(`Could not find expected total column in saved data for ${name}. Returning 0. See StoredTable.totalCol() switch.`); - return 0; - } - -} - -// Holds all the detailed data for one fund's budget -export class Fund { - constructor(fund){ - this.fund = fund; - this.personnel = new StoredTable('personnel', fund); - this.overtime = new StoredTable('overtime', fund); - this.nonpersonnel = new StoredTable('nonpersonnel', fund); - this.revenue = new StoredTable('revenue', fund); - } - - getPersonnelCost() { - return this.personnel.getSum() + this.overtime.getSum(); - } - - getNonPersonnelCost() { - return this.nonpersonnel.getSum(); - } - - getRevenue() { - return this.revenue.getSum(); - } - - getTotal() { - return this.getNonPersonnelCost() + this.getPersonnelCost() - this.getRevenue() - } -} - -export class Baseline { - // baseline will just contain a list of funds - constructor() { - const allFunds = FundLookupTable.listFunds(); - this.funds = []; - allFunds.forEach((fund) => { - this.funds.push(new Fund(fund)); - }); - } - - personnel() { - let total = 0; - this.funds.forEach(fund => { - total += fund.getPersonnelCost(); - }); - return total; - } - - nonpersonnel() { - let total = 0; - this.funds.forEach(fund => { - total += fund.getNonPersonnelCost(); - }); - return total; - } - - revenue() { - let total = 0; - this.funds.forEach(fund => { - total += fund.getRevenue(); - }); - return total; - } - - total() { - return this.nonpersonnel() + this.personnel() - this.revenue(); - } -} - -export class Initiative { - constructor(row) { - this.data = row; - this.name = row['Initiative Name']; - } - - expenses() { - if (this.data['Ballpark Total Expenses']) { - return this.data['Ballpark Total Expenses']; - } else { - return 0; - } - } - - revenue() { - if (this.data['Revenue']) { - return this.data['Revenue']; - } else { - return 0; - } - } - - net() { return this.expenses() - this.revenue() } - -} - -export class Supplemental { - constructor() { - this.table = loadTableData('new-inits'); - this.initiatives = []; - if(this.table){ - this.table.forEach((row) => { - this.initiatives.push(new Initiative(row)); - }); - } - } - - getInits() { - return this.table.map((item) => { return item['Initiative Name'] }); - } - - expenses() { - return colSum(this.table, 'Ballpark Total Expenses'); - } - - revenue() { - return colSum(this.table, 'Revenue'); - } - - total(){ - return this.expenses() - this.revenue(); - } - -} diff --git a/src/js/views/view_class.js b/src/js/views/view_class.js index 2e336f8..09b8ba0 100644 --- a/src/js/views/view_class.js +++ b/src/js/views/view_class.js @@ -7,7 +7,7 @@ import Table from "../components/table/table.js"; import Form from "../components/form/form.js"; import Modal from "../components/modal/modal.js"; -import { CurrentPage } from "../utils/data_utils/local_storage_handlers.js"; +import CurrentPage from "../models/current_page.js"; import { AccountString } from "../utils/data_utils/budget_data_handlers.js"; export class View { diff --git a/src/js/views/view_logic.js b/src/js/views/view_logic.js index 5a5bb8c..ee42a6a 100644 --- a/src/js/views/view_logic.js +++ b/src/js/views/view_logic.js @@ -8,8 +8,9 @@ import NonPersonnelView from './06_nonpersonnel.js'; import InitiativesView from './07_new_initiatives.js'; import SummaryView from './08_summary.js'; -import { CurrentPage, CurrentFund } from '../utils/data_utils/local_storage_handlers.js'; import { FundLookupTable } from '../utils/data_utils/budget_data_handlers.js'; +import CurrentFund from '../models/current_fund.js'; +import CurrentPage from '../models/current_page.js'; import { FISCAL_YEAR } from '../init.js'; export function initializePages() { From 3ac5db4b71331619fbef9f9bd341b273bd42d187 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 29 Jul 2024 14:44:31 -0400 Subject: [PATCH 18/22] fix all imports --- src/js/components/accordion/accordion.js | 7 +++++-- src/js/components/table/subcomponents/data.js | 5 ++--- src/js/components/tooltip/tooltip.js | 3 ++- src/js/models/fund.js | 3 ++- src/js/models/supplemental.js | 3 ++- src/js/utils/data_utils/XLSX_handlers.js | 3 ++- src/js/views/02_baseline_landing.js | 2 +- src/js/views/04_personnel.js | 3 ++- src/js/views/05_overtime.js | 4 +++- src/js/views/06_nonpersonnel.js | 2 +- src/js/views/07_new_initiatives.js | 2 +- src/js/views/08_summary.js | 3 ++- src/js/views/view_logic.js | 2 +- 13 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/js/components/accordion/accordion.js b/src/js/components/accordion/accordion.js index c66d348..93b9074 100644 --- a/src/js/components/accordion/accordion.js +++ b/src/js/components/accordion/accordion.js @@ -1,9 +1,12 @@ import './accordion.css' -import { Baseline, CurrentFund, Fund, Supplemental } from "../../utils/data_utils/local_storage_handlers.js"; +import Baseline from '../../models/baseline.js'; +import CurrentFund from '../../models/current_fund.js'; +import Fund from '../../models/fund.js'; +import Supplemental from '../../models/supplemental.js'; import { formatCurrency, cleanString } from "../../utils/common_utils.js"; import Table from "../table/table.js"; -import { FundLookupTable } from "../../utils/data_utils/budget_data_handlers.js"; +import FundLookupTable from '../../models/fund_lookup_table.js'; import { visitPage } from '../../views/view_logic.js'; import { TARGET } from '../../init.js'; diff --git a/src/js/components/table/subcomponents/data.js b/src/js/components/table/subcomponents/data.js index 16f2188..23c96dc 100644 --- a/src/js/components/table/subcomponents/data.js +++ b/src/js/components/table/subcomponents/data.js @@ -1,4 +1,4 @@ -import { FundLookupTable } from "../../../utils/data_utils/budget_data_handlers.js"; +import FundLookupTable from '../../../models/fund_lookup_table.js'; import CurrentFund from '../../../models/current_fund.js' import CurrentPage from '../../../models/current_page.js' @@ -45,9 +45,8 @@ async function loadFromStorage(){ } // load from local storage const data = localStorage.getItem(key); - // if nothing in storage, return a zero - if ( data == '' || data == '[]' ) { + if ( !data ) { return 0; }; // otherwise, fill table in HTML and return success (1) diff --git a/src/js/components/tooltip/tooltip.js b/src/js/components/tooltip/tooltip.js index c86dd88..09f0a4c 100644 --- a/src/js/components/tooltip/tooltip.js +++ b/src/js/components/tooltip/tooltip.js @@ -1,8 +1,9 @@ import { FISCAL_YEAR } from '../../init'; import Cell from '../table/subcomponents/cells'; import { formatCurrency } from '../../utils/common_utils'; +import CurrentPage from '../../models/current_page'; + import './tooltip.css' -import { CurrentFund, CurrentPage } from '../../utils/data_utils/local_storage_handlers'; function hideTooltip() { document.getElementById('tooltip').style.visibility = 'hidden'; diff --git a/src/js/models/fund.js b/src/js/models/fund.js index 59268c2..4d43df4 100644 --- a/src/js/models/fund.js +++ b/src/js/models/fund.js @@ -1,12 +1,13 @@ import { colSum } from "../utils/common_utils"; +import { FISCAL_YEAR } from "../init"; // Class to hold information on a specific fund and table class StoredTable { constructor(page, fund){ this.name = `${page}_${fund}`; this.page = page; - this.table = loadTableData(this.name); + this.table = JSON.parse(localStorage.getItem(this.name)); } totalCol() { diff --git a/src/js/models/supplemental.js b/src/js/models/supplemental.js index 1cfb9ca..5126324 100644 --- a/src/js/models/supplemental.js +++ b/src/js/models/supplemental.js @@ -1,10 +1,11 @@ import Initiative from "./initiative.js"; +import { colSum } from "../utils/common_utils.js"; // data structure to hold supplemental requests export class Supplemental { constructor() { - this.table = loadTableData('new-inits'); + this.table = JSON.parse(localStorage.getItem(this.name)); this.initiatives = []; if(this.table){ this.table.forEach((row) => { diff --git a/src/js/utils/data_utils/XLSX_handlers.js b/src/js/utils/data_utils/XLSX_handlers.js index 1b16ec3..3f40774 100644 --- a/src/js/utils/data_utils/XLSX_handlers.js +++ b/src/js/utils/data_utils/XLSX_handlers.js @@ -1,7 +1,8 @@ import { SHEETS } from '../../init.js'; -import { FundLookupTable, Services } from './budget_data_handlers.js'; +import FundLookupTable from '../../models/fund_lookup_table.js'; +import { Services } from './budget_data_handlers.js'; import { removeNewLines } from '../common_utils.js'; import { Baseline } from './local_storage_handlers.js'; diff --git a/src/js/views/02_baseline_landing.js b/src/js/views/02_baseline_landing.js index c72af89..53f9681 100644 --- a/src/js/views/02_baseline_landing.js +++ b/src/js/views/02_baseline_landing.js @@ -1,7 +1,7 @@ import NavButtons from "../components/nav_buttons/nav_buttons.js"; import Table from "../components/table/table.js"; import { View, ViewTable } from './view_class.js' -import { CurrentFund } from "../utils/data_utils/local_storage_handlers.js"; +import CurrentFund from "../models/current_fund.js"; export class FundView extends View { diff --git a/src/js/views/04_personnel.js b/src/js/views/04_personnel.js index 070be96..8bba500 100644 --- a/src/js/views/04_personnel.js +++ b/src/js/views/04_personnel.js @@ -3,7 +3,8 @@ import { View, ViewTable } from './view_class.js' import Table from "../components/table/table.js"; import Form from "../components/form/form.js"; -import { Services, FundLookupTable } from "../utils/data_utils/budget_data_handlers.js"; +import { Services } from "../utils/data_utils/budget_data_handlers.js"; +import FundLookupTable from '../models/fund_lookup_table.js'; import { unformatCurrency } from "../utils/common_utils.js"; export class PersonnelView extends View { diff --git a/src/js/views/05_overtime.js b/src/js/views/05_overtime.js index 96a925d..c6f32e9 100644 --- a/src/js/views/05_overtime.js +++ b/src/js/views/05_overtime.js @@ -4,7 +4,9 @@ import { View, ViewTable } from './view_class.js' import Table from '../components/table/table.js'; import Form from '../components/form/form.js'; -import { FundLookupTable, Services } from '../utils/data_utils/budget_data_handlers.js'; +import FundLookupTable from '../models/fund_lookup_table.js'; + +import { Services } from '../utils/data_utils/budget_data_handlers.js'; import { unformatCurrency } from '../utils/common_utils.js'; export class OvertimeView extends View { diff --git a/src/js/views/06_nonpersonnel.js b/src/js/views/06_nonpersonnel.js index 93f8088..c446abf 100644 --- a/src/js/views/06_nonpersonnel.js +++ b/src/js/views/06_nonpersonnel.js @@ -1,7 +1,7 @@ import { View, ViewTable } from './view_class.js' import Form from '../components/form/form.js'; import Table from '../components/table/table.js'; -import { FundLookupTable } from '../utils/data_utils/budget_data_handlers.js'; +import FundLookupTable from '../models/fund_lookup_table.js'; import { ObjectCategories, Services } from '../utils/data_utils/budget_data_handlers.js'; import { unformatCurrency } from '../utils/common_utils.js'; diff --git a/src/js/views/07_new_initiatives.js b/src/js/views/07_new_initiatives.js index 1f27cf8..d2720b1 100644 --- a/src/js/views/07_new_initiatives.js +++ b/src/js/views/07_new_initiatives.js @@ -2,7 +2,7 @@ import { View, ViewTable } from './view_class.js' import Table from "../components/table/table.js"; import Form from "../components/form/form.js"; -import { FundLookupTable } from "../utils/data_utils/budget_data_handlers.js"; +import FundLookupTable from '../models/fund_lookup_table.js'; import { FISCAL_YEAR } from "../init.js"; diff --git a/src/js/views/08_summary.js b/src/js/views/08_summary.js index d922be1..70cce64 100644 --- a/src/js/views/08_summary.js +++ b/src/js/views/08_summary.js @@ -1,4 +1,5 @@ -import { CurrentFund, Baseline } from "../utils/data_utils/local_storage_handlers.js"; +import CurrentFund from '../models/current_fund.js'; +import Baseline from '../models/baseline.js'; import { TARGET } from '../init.js'; import { Accordion } from "../components/accordion/accordion.js"; import { visitPage } from "./view_logic.js"; diff --git a/src/js/views/view_logic.js b/src/js/views/view_logic.js index ee42a6a..ad34088 100644 --- a/src/js/views/view_logic.js +++ b/src/js/views/view_logic.js @@ -8,7 +8,7 @@ import NonPersonnelView from './06_nonpersonnel.js'; import InitiativesView from './07_new_initiatives.js'; import SummaryView from './08_summary.js'; -import { FundLookupTable } from '../utils/data_utils/budget_data_handlers.js'; +import FundLookupTable from '../models/fund_lookup_table.js'; import CurrentFund from '../models/current_fund.js'; import CurrentPage from '../models/current_page.js'; import { FISCAL_YEAR } from '../init.js'; From f1663475a62b0612092995a96fd464626f871624 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 29 Jul 2024 14:55:51 -0400 Subject: [PATCH 19/22] fix all imports --- src/js/components/file_upload/file_upload.js | 2 +- .../components/table/subcomponents/cells.js | 2 +- src/js/components/table/table.js | 2 +- src/js/init.js | 15 ++++++++++ .../account_string.js} | 28 ++----------------- src/js/models/services.js | 11 ++++++++ .../{data_utils => }/JSON_data_handlers.js | 0 .../utils/{data_utils => }/XLSX_handlers.js | 10 +++---- .../data_utils/local_storage_handlers.js | 18 ------------ src/js/views/04_personnel.js | 2 +- src/js/views/05_overtime.js | 2 +- src/js/views/06_nonpersonnel.js | 3 +- src/js/views/08_summary.js | 2 +- src/js/views/view_class.js | 3 +- 14 files changed, 44 insertions(+), 56 deletions(-) rename src/js/{utils/data_utils/budget_data_handlers.js => models/account_string.js} (67%) create mode 100644 src/js/models/services.js rename src/js/utils/{data_utils => }/JSON_data_handlers.js (100%) rename src/js/utils/{data_utils => }/XLSX_handlers.js (95%) delete mode 100644 src/js/utils/data_utils/local_storage_handlers.js diff --git a/src/js/components/file_upload/file_upload.js b/src/js/components/file_upload/file_upload.js index 43cc3d4..719ba59 100644 --- a/src/js/components/file_upload/file_upload.js +++ b/src/js/components/file_upload/file_upload.js @@ -1,6 +1,6 @@ import './file_upload.css'; -import { processWorkbook } from "../../utils/data_utils/XLSX_handlers.js"; +import { processWorkbook } from "../../utils/XLSX_handlers.js"; import Sidebar from '../sidebar/sidebar.js'; export const FileUpload = { diff --git a/src/js/components/table/subcomponents/cells.js b/src/js/components/table/subcomponents/cells.js index 73e8eec..67d13b0 100644 --- a/src/js/components/table/subcomponents/cells.js +++ b/src/js/components/table/subcomponents/cells.js @@ -1,5 +1,5 @@ import { formatCurrency, displayWithCommas } from "../../../utils/common_utils.js"; -import { Services } from "../../../utils/data_utils/budget_data_handlers.js"; +import Services from "../../../models/services.js"; import Dropdown from "../../form/subcomponents/dropdown.js"; // return cell value attribute or 0 if it does not exist diff --git a/src/js/components/table/table.js b/src/js/components/table/table.js index 9986323..8ecf231 100644 --- a/src/js/components/table/table.js +++ b/src/js/components/table/table.js @@ -7,7 +7,7 @@ import Header from './subcomponents/headers.js' import Rows from './subcomponents/rows.js' import Data from './subcomponents/data.js' import Tooltip from '../tooltip/tooltip.js'; -import { convertToJSON } from "../../utils/data_utils/JSON_data_handlers.js"; +import { convertToJSON } from "../../utils/JSON_data_handlers.js"; import Sidebar from '../sidebar/sidebar.js'; import CurrentFund from '../../models/current_fund.js'; import CurrentPage from '../../models/current_page.js'; diff --git a/src/js/init.js b/src/js/init.js index e079cef..e70d7e2 100644 --- a/src/js/init.js +++ b/src/js/init.js @@ -17,6 +17,21 @@ export const SHEETS = { 'Revenue' : 'revenue' } +export const ObjectCategories = { + list : [ + // 'Salaries & Wages', + // 'Employee Benefits', + 'Professional & Contractual Services', + 'Operating Supplies', + 'Operating Services', + 'Equipment Acquisition', + 'Capital Outlays', + 'Fixed Charges', + 'Other Expenses' + ] +} + + document.addEventListener('DOMContentLoaded', function () { CurrentPage.visit(); }); diff --git a/src/js/utils/data_utils/budget_data_handlers.js b/src/js/models/account_string.js similarity index 67% rename from src/js/utils/data_utils/budget_data_handlers.js rename to src/js/models/account_string.js index 6b3b64e..cf8095a 100644 --- a/src/js/utils/data_utils/budget_data_handlers.js +++ b/src/js/models/account_string.js @@ -1,29 +1,5 @@ -// data structure to save the possible service options for the department -export const Services = { - save : function(services){ - localStorage.setItem('services-list', JSON.stringify(services)); - }, - list : function(){ - return JSON.parse(localStorage.getItem('services-list')) || {}; - } -} - -export const ObjectCategories = { - list : [ - // 'Salaries & Wages', - // 'Employee Benefits', - 'Professional & Contractual Services', - 'Operating Supplies', - 'Operating Services', - 'Equipment Acquisition', - 'Capital Outlays', - 'Fixed Charges', - 'Other Expenses' - ] -} - export const AccountString = { getNumber: function(input) { // isolate the numerical part of a appropriation/cost center/object @@ -62,4 +38,6 @@ export const AccountString = { object : function(account_string) { return this.getAccountStringSection(account_string, 3) }, -} \ No newline at end of file +} + +export default AccountString; \ No newline at end of file diff --git a/src/js/models/services.js b/src/js/models/services.js new file mode 100644 index 0000000..9cf60e0 --- /dev/null +++ b/src/js/models/services.js @@ -0,0 +1,11 @@ +// data structure to save the possible service options for the department +export const Services = { + save : function(services){ + localStorage.setItem('services-list', JSON.stringify(services)); + }, + list : function(){ + return JSON.parse(localStorage.getItem('services-list')) || {}; + } +} + +export default Services; \ No newline at end of file diff --git a/src/js/utils/data_utils/JSON_data_handlers.js b/src/js/utils/JSON_data_handlers.js similarity index 100% rename from src/js/utils/data_utils/JSON_data_handlers.js rename to src/js/utils/JSON_data_handlers.js diff --git a/src/js/utils/data_utils/XLSX_handlers.js b/src/js/utils/XLSX_handlers.js similarity index 95% rename from src/js/utils/data_utils/XLSX_handlers.js rename to src/js/utils/XLSX_handlers.js index 3f40774..3548605 100644 --- a/src/js/utils/data_utils/XLSX_handlers.js +++ b/src/js/utils/XLSX_handlers.js @@ -1,10 +1,10 @@ -import { SHEETS } from '../../init.js'; -import FundLookupTable from '../../models/fund_lookup_table.js'; -import { Services } from './budget_data_handlers.js'; -import { removeNewLines } from '../common_utils.js'; -import { Baseline } from './local_storage_handlers.js'; +import { SHEETS } from '../init.js'; +import FundLookupTable from '../models/fund_lookup_table.js'; +import { removeNewLines } from './common_utils.js'; +import Baseline from '../models/baseline.js'; +import Services from '../models/services.js'; function deleteTopRowsUntilFullData(data) { // function to try to find the top of the usable data diff --git a/src/js/utils/data_utils/local_storage_handlers.js b/src/js/utils/data_utils/local_storage_handlers.js deleted file mode 100644 index 644a197..0000000 --- a/src/js/utils/data_utils/local_storage_handlers.js +++ /dev/null @@ -1,18 +0,0 @@ -// import { initializePages } from "../../views/view_logic.js"; -// import { fetchJSON } from "./JSON_data_handlers.js"; - -// function deleteTable(name){ -// localStorage.setItem(name, ''); -// } - -// export async function deleteAllTables(){ -// var funds = await fetchJSON(DATA_ROOT + 'funds.json'); -// funds = funds.map((item) => { return item.Name }); -// for (const page in initializePages()){ -// for(const i in funds){ -// deleteTable(`${page}_${funds[i]}`); -// } -// } -// deleteTable('new-inits'); -// } - diff --git a/src/js/views/04_personnel.js b/src/js/views/04_personnel.js index 8bba500..a93f4b0 100644 --- a/src/js/views/04_personnel.js +++ b/src/js/views/04_personnel.js @@ -3,7 +3,7 @@ import { View, ViewTable } from './view_class.js' import Table from "../components/table/table.js"; import Form from "../components/form/form.js"; -import { Services } from "../utils/data_utils/budget_data_handlers.js"; +import Services from '../models/services.js'; import FundLookupTable from '../models/fund_lookup_table.js'; import { unformatCurrency } from "../utils/common_utils.js"; diff --git a/src/js/views/05_overtime.js b/src/js/views/05_overtime.js index c6f32e9..82816b0 100644 --- a/src/js/views/05_overtime.js +++ b/src/js/views/05_overtime.js @@ -6,7 +6,7 @@ import Form from '../components/form/form.js'; import FundLookupTable from '../models/fund_lookup_table.js'; -import { Services } from '../utils/data_utils/budget_data_handlers.js'; +import Services from '../models/services.js'; import { unformatCurrency } from '../utils/common_utils.js'; export class OvertimeView extends View { diff --git a/src/js/views/06_nonpersonnel.js b/src/js/views/06_nonpersonnel.js index c446abf..dc44be2 100644 --- a/src/js/views/06_nonpersonnel.js +++ b/src/js/views/06_nonpersonnel.js @@ -2,8 +2,9 @@ import { View, ViewTable } from './view_class.js' import Form from '../components/form/form.js'; import Table from '../components/table/table.js'; import FundLookupTable from '../models/fund_lookup_table.js'; -import { ObjectCategories, Services } from '../utils/data_utils/budget_data_handlers.js'; import { unformatCurrency } from '../utils/common_utils.js'; +import { ObjectCategories } from '../init.js'; +import Services from '../models/services.js'; export class NonPersonnelView extends View { diff --git a/src/js/views/08_summary.js b/src/js/views/08_summary.js index 70cce64..88267f7 100644 --- a/src/js/views/08_summary.js +++ b/src/js/views/08_summary.js @@ -6,7 +6,7 @@ import { visitPage } from "./view_logic.js"; import { formatCurrency } from '../utils/common_utils.js'; import { View } from "./view_class.js"; import Prompt from "../components/prompt/prompt.js"; -import { downloadXLSX } from "../utils/data_utils/XLSX_handlers.js"; +import { downloadXLSX } from "../utils/XLSX_handlers.js"; export function compareToTarget(){ const baseline = new Baseline; diff --git a/src/js/views/view_class.js b/src/js/views/view_class.js index 09b8ba0..813dd43 100644 --- a/src/js/views/view_class.js +++ b/src/js/views/view_class.js @@ -8,7 +8,8 @@ import Form from "../components/form/form.js"; import Modal from "../components/modal/modal.js"; import CurrentPage from "../models/current_page.js"; -import { AccountString } from "../utils/data_utils/budget_data_handlers.js"; +import AccountString from '../models/account_string.js' + export class View { From 4aea29c3f614d26710eb7a35cdac4361a6d98e6b Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 29 Jul 2024 15:16:56 -0400 Subject: [PATCH 20/22] fix welcome page bug --- src/js/views/00_welcome.js | 8 ++++---- src/js/views/08_summary.js | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/js/views/00_welcome.js b/src/js/views/00_welcome.js index 7a97ae8..c16b037 100644 --- a/src/js/views/00_welcome.js +++ b/src/js/views/00_welcome.js @@ -20,10 +20,10 @@ export class WelcomeView extends View { Welcome.show(); // initialize links in buttons - document.getElementById('step-upload').addEventListener('click', visitPage('upload')); - document.getElementById('step-initiatives').addEventListener('click', visitPage('new-inits')) - document.getElementById('step-revenue').addEventListener('click', visitPage('revenue')) - document.getElementById('step-finish').addEventListener('click', visitPage('summary')) + document.getElementById('step-upload').addEventListener('click', () => visitPage('upload')); + document.getElementById('step-initiatives').addEventListener('click', () => visitPage('new-inits')); + document.getElementById('step-revenue').addEventListener('click', () => visitPage('revenue')); + document.getElementById('step-finish').addEventListener('click', () => visitPage('summary')); } diff --git a/src/js/views/08_summary.js b/src/js/views/08_summary.js index 88267f7..7ecce7e 100644 --- a/src/js/views/08_summary.js +++ b/src/js/views/08_summary.js @@ -7,6 +7,7 @@ import { formatCurrency } from '../utils/common_utils.js'; import { View } from "./view_class.js"; import Prompt from "../components/prompt/prompt.js"; import { downloadXLSX } from "../utils/XLSX_handlers.js"; +import WelcomeView from './00_welcome.js'; export function compareToTarget(){ const baseline = new Baseline; @@ -22,7 +23,10 @@ export function compareToTarget(){ Prompt.show(); } -const returnToWelcome = () => {visitPage('welcome')}; +function returnToWelcome() { + const welcome = new WelcomeView(); + welcome.visit(); +}; export class SummaryView extends View { From 125c981dbc0f9a96aecd6bc17c10651a9ed59d89 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 29 Jul 2024 15:40:19 -0400 Subject: [PATCH 21/22] create index.js for models/ to consolidate imports --- src/js/components/accordion/accordion.js | 7 ++----- src/js/components/modal/modal.js | 1 - src/js/components/sidebar/sidebar.js | 4 ++-- src/js/models/index.js | 11 +++++++++++ src/js/views/04_personnel.js | 3 +-- src/js/views/05_overtime.js | 4 +--- src/js/views/06_nonpersonnel.js | 3 +-- src/js/views/08_summary.js | 2 ++ src/js/views/view_class.js | 3 +-- src/js/views/view_logic.js | 4 +--- 10 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 src/js/models/index.js diff --git a/src/js/components/accordion/accordion.js b/src/js/components/accordion/accordion.js index 93b9074..5b9c7f0 100644 --- a/src/js/components/accordion/accordion.js +++ b/src/js/components/accordion/accordion.js @@ -1,12 +1,9 @@ import './accordion.css' -import Baseline from '../../models/baseline.js'; -import CurrentFund from '../../models/current_fund.js'; -import Fund from '../../models/fund.js'; -import Supplemental from '../../models/supplemental.js'; + +import {Baseline, CurrentFund, Fund, Supplemental, FundLookupTable} from '../../models' import { formatCurrency, cleanString } from "../../utils/common_utils.js"; import Table from "../table/table.js"; -import FundLookupTable from '../../models/fund_lookup_table.js'; import { visitPage } from '../../views/view_logic.js'; import { TARGET } from '../../init.js'; diff --git a/src/js/components/modal/modal.js b/src/js/components/modal/modal.js index 3099ca3..14fd47b 100644 --- a/src/js/components/modal/modal.js +++ b/src/js/components/modal/modal.js @@ -4,7 +4,6 @@ import './modal.css'; function clearModal(){ updateModalTitle(''); document.getElementById('modal-body').innerHTML = ''; - //removeAllModalLinks() } function hideModal(modal_id) { diff --git a/src/js/components/sidebar/sidebar.js b/src/js/components/sidebar/sidebar.js index a867a78..057b518 100644 --- a/src/js/components/sidebar/sidebar.js +++ b/src/js/components/sidebar/sidebar.js @@ -2,8 +2,8 @@ import './sidebar.css' import { formatCurrency } from "../../utils/common_utils.js"; import { TARGET } from "../../init.js"; -import Baseline from '../../models/baseline.js' -import Supplemental from '../../models/supplemental.js' +import {Baseline, Supplemental} from '../../models/'; + // Assuming you have a CSS variable --main-color defined on the :root const root = document.documentElement; diff --git a/src/js/models/index.js b/src/js/models/index.js new file mode 100644 index 0000000..f2025bf --- /dev/null +++ b/src/js/models/index.js @@ -0,0 +1,11 @@ +// models/index.js + +export { default as AccountString } from './account_string.js'; +export { default as Baseline } from './baseline.js'; +export { default as CurrentFund } from './current_fund.js'; +export { default as CurrentPage } from './current_page.js'; +export { default as Fund } from './fund.js'; +export { default as FundLookupTable } from './fund_lookup_table.js'; +export { default as Initiative } from './initiative.js'; +export { default as Services } from './services.js'; +export { default as Supplemental } from './supplemental.js'; diff --git a/src/js/views/04_personnel.js b/src/js/views/04_personnel.js index a93f4b0..00deb60 100644 --- a/src/js/views/04_personnel.js +++ b/src/js/views/04_personnel.js @@ -3,8 +3,7 @@ import { View, ViewTable } from './view_class.js' import Table from "../components/table/table.js"; import Form from "../components/form/form.js"; -import Services from '../models/services.js'; -import FundLookupTable from '../models/fund_lookup_table.js'; +import { Services, FundLookupTable } from '../models/'; import { unformatCurrency } from "../utils/common_utils.js"; export class PersonnelView extends View { diff --git a/src/js/views/05_overtime.js b/src/js/views/05_overtime.js index 82816b0..44a9237 100644 --- a/src/js/views/05_overtime.js +++ b/src/js/views/05_overtime.js @@ -4,9 +4,7 @@ import { View, ViewTable } from './view_class.js' import Table from '../components/table/table.js'; import Form from '../components/form/form.js'; -import FundLookupTable from '../models/fund_lookup_table.js'; - -import Services from '../models/services.js'; +import { FundLookupTable, Services } from '../models/'; import { unformatCurrency } from '../utils/common_utils.js'; export class OvertimeView extends View { diff --git a/src/js/views/06_nonpersonnel.js b/src/js/views/06_nonpersonnel.js index dc44be2..6c055d0 100644 --- a/src/js/views/06_nonpersonnel.js +++ b/src/js/views/06_nonpersonnel.js @@ -1,10 +1,9 @@ import { View, ViewTable } from './view_class.js' import Form from '../components/form/form.js'; import Table from '../components/table/table.js'; -import FundLookupTable from '../models/fund_lookup_table.js'; +import { FundLookupTable, Services } from '../models/'; import { unformatCurrency } from '../utils/common_utils.js'; import { ObjectCategories } from '../init.js'; -import Services from '../models/services.js'; export class NonPersonnelView extends View { diff --git a/src/js/views/08_summary.js b/src/js/views/08_summary.js index 7ecce7e..05825fa 100644 --- a/src/js/views/08_summary.js +++ b/src/js/views/08_summary.js @@ -25,6 +25,8 @@ export function compareToTarget(){ function returnToWelcome() { const welcome = new WelcomeView(); + const left = document.getElementById('option1'); + console.log(left.l) welcome.visit(); }; diff --git a/src/js/views/view_class.js b/src/js/views/view_class.js index 813dd43..6a4b0d3 100644 --- a/src/js/views/view_class.js +++ b/src/js/views/view_class.js @@ -7,8 +7,7 @@ import Table from "../components/table/table.js"; import Form from "../components/form/form.js"; import Modal from "../components/modal/modal.js"; -import CurrentPage from "../models/current_page.js"; -import AccountString from '../models/account_string.js' +import { CurrentPage, AccountString } from '../models/' export class View { diff --git a/src/js/views/view_logic.js b/src/js/views/view_logic.js index ad34088..d81304d 100644 --- a/src/js/views/view_logic.js +++ b/src/js/views/view_logic.js @@ -8,9 +8,7 @@ import NonPersonnelView from './06_nonpersonnel.js'; import InitiativesView from './07_new_initiatives.js'; import SummaryView from './08_summary.js'; -import FundLookupTable from '../models/fund_lookup_table.js'; -import CurrentFund from '../models/current_fund.js'; -import CurrentPage from '../models/current_page.js'; +import { FundLookupTable, CurrentFund, CurrentPage } from '../models/'; import { FISCAL_YEAR } from '../init.js'; export function initializePages() { From b0a647d90c53899c67b0a090cdfee8257d52b9b5 Mon Sep 17 00:00:00 2001 From: Katrina Wheelan Date: Mon, 29 Jul 2024 15:51:44 -0400 Subject: [PATCH 22/22] move constants to own folder --- src/js/components/accordion/accordion.js | 2 +- src/js/components/sidebar/sidebar.js | 2 +- src/js/components/tooltip/tooltip.js | 2 +- src/js/constants/app_constants.js | 5 +++++ src/js/constants/excel_constants.js | 21 ++++++++++++++++++ src/js/constants/index.js | 2 ++ src/js/init.js | 28 ------------------------ src/js/models/fund.js | 2 +- src/js/utils/XLSX_handlers.js | 2 +- src/js/views/06_nonpersonnel.js | 4 ++-- src/js/views/07_new_initiatives.js | 2 +- src/js/views/08_summary.js | 2 +- src/js/views/view_logic.js | 2 +- 13 files changed, 38 insertions(+), 38 deletions(-) create mode 100644 src/js/constants/app_constants.js create mode 100644 src/js/constants/excel_constants.js create mode 100644 src/js/constants/index.js diff --git a/src/js/components/accordion/accordion.js b/src/js/components/accordion/accordion.js index 5b9c7f0..a6b7228 100644 --- a/src/js/components/accordion/accordion.js +++ b/src/js/components/accordion/accordion.js @@ -5,7 +5,7 @@ import {Baseline, CurrentFund, Fund, Supplemental, FundLookupTable} from '../../ import { formatCurrency, cleanString } from "../../utils/common_utils.js"; import Table from "../table/table.js"; import { visitPage } from '../../views/view_logic.js'; -import { TARGET } from '../../init.js'; +import { TARGET } from '../../constants/'; function redirectForEdit(){ const row = document.querySelector(`.active-editing`); diff --git a/src/js/components/sidebar/sidebar.js b/src/js/components/sidebar/sidebar.js index 057b518..bc21eef 100644 --- a/src/js/components/sidebar/sidebar.js +++ b/src/js/components/sidebar/sidebar.js @@ -1,7 +1,7 @@ import './sidebar.css' import { formatCurrency } from "../../utils/common_utils.js"; -import { TARGET } from "../../init.js"; +import { TARGET } from '../../constants/'; import {Baseline, Supplemental} from '../../models/'; diff --git a/src/js/components/tooltip/tooltip.js b/src/js/components/tooltip/tooltip.js index 09f0a4c..6967848 100644 --- a/src/js/components/tooltip/tooltip.js +++ b/src/js/components/tooltip/tooltip.js @@ -1,4 +1,4 @@ -import { FISCAL_YEAR } from '../../init'; +import { FISCAL_YEAR } from '../../constants/'; import Cell from '../table/subcomponents/cells'; import { formatCurrency } from '../../utils/common_utils'; import CurrentPage from '../../models/current_page'; diff --git a/src/js/constants/app_constants.js b/src/js/constants/app_constants.js new file mode 100644 index 0000000..225fd4d --- /dev/null +++ b/src/js/constants/app_constants.js @@ -0,0 +1,5 @@ +// temporary hard-coding +export let TARGET = 10000000; + +// Set to equal current fiscal year +export var FISCAL_YEAR = '26'; \ No newline at end of file diff --git a/src/js/constants/excel_constants.js b/src/js/constants/excel_constants.js new file mode 100644 index 0000000..0034e4c --- /dev/null +++ b/src/js/constants/excel_constants.js @@ -0,0 +1,21 @@ +// sheets to expect on detail sheet +export const SHEETS = { + 'FTE, Salary-Wage, & Benefits' : 'personnel' , + 'Overtime & Other Personnel' : 'overtime', + 'Non-Personnel Operating' : 'nonpersonnel', + 'Revenue' : 'revenue' +} + +export const OBJ_CATEGORIES = { + list : [ + // 'Salaries & Wages', + // 'Employee Benefits', + 'Professional & Contractual Services', + 'Operating Supplies', + 'Operating Services', + 'Equipment Acquisition', + 'Capital Outlays', + 'Fixed Charges', + 'Other Expenses' + ] +} \ No newline at end of file diff --git a/src/js/constants/index.js b/src/js/constants/index.js new file mode 100644 index 0000000..06b365c --- /dev/null +++ b/src/js/constants/index.js @@ -0,0 +1,2 @@ +export * from './app_constants'; +export * from './excel_constants'; \ No newline at end of file diff --git a/src/js/init.js b/src/js/init.js index e70d7e2..9e4153f 100644 --- a/src/js/init.js +++ b/src/js/init.js @@ -1,37 +1,9 @@ // import styles import '../css/common.css'; -// temporary hard-coding -export let TARGET = 10000000; -// Set to equal current fiscal year -export var FISCAL_YEAR = '26'; - // import functions import CurrentPage from './models/current_page.js'; -// sheets to expect on detail sheet -export const SHEETS = { - 'FTE, Salary-Wage, & Benefits' : 'personnel' , - 'Overtime & Other Personnel' : 'overtime', - 'Non-Personnel Operating' : 'nonpersonnel', - 'Revenue' : 'revenue' -} - -export const ObjectCategories = { - list : [ - // 'Salaries & Wages', - // 'Employee Benefits', - 'Professional & Contractual Services', - 'Operating Supplies', - 'Operating Services', - 'Equipment Acquisition', - 'Capital Outlays', - 'Fixed Charges', - 'Other Expenses' - ] -} - - document.addEventListener('DOMContentLoaded', function () { CurrentPage.visit(); }); diff --git a/src/js/models/fund.js b/src/js/models/fund.js index 4d43df4..848df5a 100644 --- a/src/js/models/fund.js +++ b/src/js/models/fund.js @@ -1,6 +1,6 @@ import { colSum } from "../utils/common_utils"; -import { FISCAL_YEAR } from "../init"; +import { FISCAL_YEAR } from '../constants/'; // Class to hold information on a specific fund and table class StoredTable { diff --git a/src/js/utils/XLSX_handlers.js b/src/js/utils/XLSX_handlers.js index 3548605..405dccb 100644 --- a/src/js/utils/XLSX_handlers.js +++ b/src/js/utils/XLSX_handlers.js @@ -1,6 +1,6 @@ -import { SHEETS } from '../init.js'; +import { SHEETS } from '../constants/'; import FundLookupTable from '../models/fund_lookup_table.js'; import { removeNewLines } from './common_utils.js'; import Baseline from '../models/baseline.js'; diff --git a/src/js/views/06_nonpersonnel.js b/src/js/views/06_nonpersonnel.js index 6c055d0..16bbeab 100644 --- a/src/js/views/06_nonpersonnel.js +++ b/src/js/views/06_nonpersonnel.js @@ -3,7 +3,7 @@ import Form from '../components/form/form.js'; import Table from '../components/table/table.js'; import { FundLookupTable, Services } from '../models/'; import { unformatCurrency } from '../utils/common_utils.js'; -import { ObjectCategories } from '../init.js'; +import { OBJ_CATEGORIES } from '../constants/'; export class NonPersonnelView extends View { @@ -52,7 +52,7 @@ class NonPersonnelTable extends ViewTable { // form questions to add a new row Form.NewField.dropdown('Appropriation:', 'approp-name', FundLookupTable.getApprops(), true); Form.NewField.dropdown('Cost Center:', 'cc-name', FundLookupTable.getCostCenters(), true); - Form.NewField.dropdown('Object Category:', 'object-category', ObjectCategories.list, true); + Form.NewField.dropdown('Object Category:', 'object-category', OBJ_CATEGORIES.list, true); // TODO: maybe give dropdown based on selected obj category Form.NewField.shortText('Object Number (if known):', 'object', false); Form.NewField.dropdown('Service', 'service', Services.list(), true); diff --git a/src/js/views/07_new_initiatives.js b/src/js/views/07_new_initiatives.js index d2720b1..421f1ce 100644 --- a/src/js/views/07_new_initiatives.js +++ b/src/js/views/07_new_initiatives.js @@ -3,7 +3,7 @@ import { View, ViewTable } from './view_class.js' import Table from "../components/table/table.js"; import Form from "../components/form/form.js"; import FundLookupTable from '../models/fund_lookup_table.js'; -import { FISCAL_YEAR } from "../init.js"; +import { FISCAL_YEAR } from '../constants/'; const dropdownOptions = ['N/A', 'One-Time', 'Recurring'] diff --git a/src/js/views/08_summary.js b/src/js/views/08_summary.js index 05825fa..dabcc18 100644 --- a/src/js/views/08_summary.js +++ b/src/js/views/08_summary.js @@ -1,6 +1,5 @@ import CurrentFund from '../models/current_fund.js'; import Baseline from '../models/baseline.js'; -import { TARGET } from '../init.js'; import { Accordion } from "../components/accordion/accordion.js"; import { visitPage } from "./view_logic.js"; import { formatCurrency } from '../utils/common_utils.js'; @@ -8,6 +7,7 @@ import { View } from "./view_class.js"; import Prompt from "../components/prompt/prompt.js"; import { downloadXLSX } from "../utils/XLSX_handlers.js"; import WelcomeView from './00_welcome.js'; +import { TARGET } from '../constants/app_constants.js'; export function compareToTarget(){ const baseline = new Baseline; diff --git a/src/js/views/view_logic.js b/src/js/views/view_logic.js index d81304d..d72d739 100644 --- a/src/js/views/view_logic.js +++ b/src/js/views/view_logic.js @@ -9,7 +9,7 @@ import InitiativesView from './07_new_initiatives.js'; import SummaryView from './08_summary.js'; import { FundLookupTable, CurrentFund, CurrentPage } from '../models/'; -import { FISCAL_YEAR } from '../init.js'; +import { FISCAL_YEAR } from '../constants/'; export function initializePages() { const PAGES = {