Skip to content

Commit

Permalink
started implementing service dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
katrina-cityofdetroit committed Jun 20, 2024
1 parent 3a491a4 commit 5f44387
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 230 deletions.
9 changes: 8 additions & 1 deletion data/law_dept_sample/services.json
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
["Appeals", "FOIA", "Lobbying"]
[
{ "id" : "Appeals",
"name" : "Appeals"},
{ "id" : "FOIA",
"name" : "FOIA" },
{ "id" : "Lobbying",
"name" : "Lobbying"}
]
20 changes: 20 additions & 0 deletions js/components/form/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,24 @@ export function addForm(element_id = 'modal-body', form_id = 'new-form') {
// Append the form to the modal body
target_elem.appendChild(form);

}

export async function createDropdownFromJSON(json_path) {
// Fetch JSON data from a file asynchronously
const response = await fetch(json_path);
const dataArray = await response.json();

// Creating a select element
const selectElement = document.createElement('select');

// Looping through the array and creating an option for each element
dataArray.forEach(item => {
const optionElement = document.createElement('option');
optionElement.value = item.id; // Setting the option value to the item id
optionElement.textContent = item.name; // Setting the display text to the item name
selectElement.appendChild(optionElement); // Appending the option to the select
});

// Return the select element so it can be appended to the document
return selectElement.outerHTML;
}
7 changes: 7 additions & 0 deletions js/components/sidebar/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,17 @@ export function updateTotals() {
fetchStat('baseline-nonpersonnel');
replaceSidebarStat('supp-total', supp_total);
replaceSidebarStat('baseline-total', baseline_total);

// color code based on target
var target = fetchStat('target');
if(baseline_total <= target){
document.querySelector('#baseline-total .stat').style.color = "green";
}
if(baseline_total > target){
document.getElementById('#baseline-total .stat').style.color = "red";
}
}

export function addTarget(target){
replaceSidebarStat('target', target);
}
13 changes: 3 additions & 10 deletions js/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,15 @@ import { loadRevenuePage } from './pages/03_revenue/main.js'
import { loadPageState } from './utils/storage-handlers.js'
import { initializeNavButtons } from './components/nav_buttons/nav_buttons.js';
import { loadPersonnelPage } from './pages/04_personnel/main.js';
import { addTarget } from './components/sidebar/sidebar.js';

// running tallies of total spend
let personnel_supp = 0;
let personnel_baseline = 0;
let nonpersonnel_supp = 0;
let nonpersonnel_baseline = 0;
let target = 2000000;
let baseline_revenue = 0;
let supp_revenue = 0;
let supp_total = personnel_supp - supp_revenue;
let baseline_total = personnel_baseline - baseline_revenue;
export let DATA_ROOT = '../../../data/law_dept_sample/'

document.addEventListener('DOMContentLoaded', function () {

var page_state = loadPageState();
initializeNavButtons();
addTarget(2000000);

switch (page_state){
case 'welcome':
Expand Down
171 changes: 171 additions & 0 deletions js/pages/04_personnel/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { hideWelcomeButtons } from "../../components/welcome/welcome.js";
import { hidePromptButtons, showPrompt, updatePrompt } from "../../components/prompt/prompt.js";
import { showNavButtons } from "../../components/nav_buttons/nav_buttons.js";
import { updateSubtitle } from "../../components/header/header.js";
import { loadJSONIntoTable } from "../../utils/data-handlers.js";
import { AddCostClass, addCol, addColToEnd, addEditCol, adjustTableWidth, assignClassToColumn, showTable } from "../../components/table/table.js";
import { incrementSidebarStat, showSideBar } from "../../components/sidebar/sidebar.js";
import { formatCurrency } from "../../utils/utils.js";
import { DATA_ROOT } from "../../init.js"
import { createDropdownFromJSON } from "../../components/form/form.js";

// variables on the salary
var fringe = 0.36
var cola = 0.02
var merit = 0.02

export function preparePageView(){
// prepare page view
hideWelcomeButtons();
showPrompt();
showNavButtons();
showSideBar();
hidePromptButtons();
adjustTableWidth('main-table', '90%');

// update page text
updateSubtitle('Personnel');
updatePrompt('For each job in your department, select the service and request the number of baseline and supplemental FTEs.');
}

export async function initializePersonnelTable(){
// load table data from json
await loadJSONIntoTable(DATA_ROOT + 'personnel_data.json', 'main-table');
//after table is loaded, fill it
showTable('main-table');
addCol('main-table', 3, '', 'Service');
addColToEnd('main-table', '0', 'Total Cost (Baseline)');
addColToEnd('main-table', '0', 'Total Cost (Supplementary)');
addEditCol('main-table');
// assign cost classes
assignClassToColumn('main-table', 'Current Average Salary', 'avg-salary');
AddCostClass('main-table', 'Current Average Salary');
assignClassToColumn('main-table', 'Total Cost (Baseline)', 'total-baseline');
AddCostClass('main-table', 'Total Cost (Baseline)');
assignClassToColumn('main-table', 'Total Cost (Supplementary)', 'total-supp');
AddCostClass('main-table', 'Total Cost (Supplementary)');
// assign other classes
assignClassToColumn('main-table', 'Job Name', 'job-name');
assignClassToColumn('main-table', 'Baseline FTEs', 'baseline-ftes');
assignClassToColumn('main-table', 'Supplemental FTEs', 'supp-ftes');
assignClassToColumn('main-table', 'Service', 'service');
// manage edit buttons
handleRowEdit();
}

export function handleRowEdit(){
// attach an event listener to each edit button in every row
var editButtons = document.getElementsByClassName('btn-edit');
for (var i = 0; i < editButtons.length; i++) {
editButtons[i].addEventListener('click', async function(event) {
// Determine what was clicked on within the table
var rowToEdit = event.target.closest('tr');
// mark row as being edited
rowToEdit.classList.add('active-editing');

// turn relevant entries into textboxes
createEditableCell('baseline-ftes');
createEditableCell('supp-ftes');
// add service dropdown
const serviceDropdown = await createDropdownFromJSON(DATA_ROOT + 'services.json');
rowToEdit.querySelector('.service').innerHTML = serviceDropdown;

// hide edit buttons
var editButtons = document.getElementsByClassName('btn-edit');
for (var i = 0; i < editButtons.length; i++) {
editButtons[i].style.display = 'none';
}

initializeConfirmButton(rowToEdit);
});
};
}


function createEditableCell(cellClass, attribute = 'value'){
// get cell
const cell = document.querySelector(`.active-editing td.${cellClass}`);
// Create an input element to edit the value
var textbox = document.createElement('input');
textbox.type = 'text';
textbox.value = cell.textContent;
// Clear the current content and append the textbox to the cell
cell.innerHTML = '';
cell.appendChild(textbox);
//cell.appendChild(feedback);
}


function initializeConfirmButton(rowToEdit){
// get element and add listener for click
const confirm_btn = rowToEdit.querySelector(".btn-confirm");
// show confirm button
confirm_btn.style.display = 'block';
confirm_btn.addEventListener('click', function(event){
// get current row
const rowToEdit = event.target.closest('tr');
var textboxes = rowToEdit.querySelectorAll('input')
// save all text in textboxes
textboxes.forEach( textbox => {
var enteredValue = textbox.value;
var cell = textbox.parentElement;
cell.textContent = enteredValue;
cell.setAttribute('value', enteredValue);
})

//set service value


// update values in sidebar
updateDisplayandTotals();

// make row no longer green
rowToEdit.classList.remove('active-editing');

// show edit buttons
var editButtons = document.getElementsByClassName('btn-edit');
for (var i = 0; i < editButtons.length; i++) {
editButtons[i].style.display = 'block';
}

// hide confirm button
confirm_btn.style.display = 'none';
});
}

function getCellValue(row, className){
var cellValue = row.querySelector(`.${className}`).getAttribute('value');
return parseFloat(cellValue);
}

function calculateTotalCost(ftes, avg_salary, fringe, cola, merit){
return ftes * avg_salary * (1 + fringe) * (1 + cola) * (1 + merit);
}

export function updateTableCell(row, col_class, new_value){
const cell = row.querySelector(`.${col_class}`);
cell.setAttribute('value', new_value);
cell.textContent = formatCurrency(new_value);
}

// update sidebar and also cost totals when the FTEs are edited
function updateDisplayandTotals(){
// get row
const row = document.querySelector('.active-editing');
// fetch values for calculations
let avg_salary = getCellValue(row, 'avg-salary');
let baseline_ftes = getCellValue(row, 'baseline-ftes');
let supp_ftes = getCellValue(row, 'supp-ftes');

// calcuate #FTEs x average salary + COLA adjustments + merit adjustments + fringe
let total_baseline_cost = calculateTotalCost(baseline_ftes, avg_salary, fringe, cola, merit);
let total_supp_cost = calculateTotalCost(supp_ftes, avg_salary, fringe, cola, merit);

// update counters
incrementSidebarStat('baseline-personnel', total_baseline_cost);
incrementSidebarStat('supp-personnel', total_supp_cost);

// update totals in table
updateTableCell(row, 'total-baseline', total_baseline_cost);
updateTableCell(row, 'total-supp', total_supp_cost);
}
Loading

0 comments on commit 5f44387

Please sign in to comment.