diff --git a/package-lock.json b/package-lock.json index 99e41198..8d270ea3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2608,6 +2608,29 @@ "gud": "^1.0.0" } }, + "cross-env": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.0.tgz", + "integrity": "sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==", + "requires": { + "cross-spawn": "^6.0.5", + "is-windows": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -5768,6 +5791,11 @@ "minimatch": "^3.0.4" } }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, "immutable": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", @@ -6951,6 +6979,14 @@ "type-check": "~0.3.2" } }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "requires": { + "immediate": "~3.0.5" + } + }, "lint-staged": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.0.4.tgz", @@ -7647,6 +7683,14 @@ "json5": "^0.5.0" } }, + "localforage": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.3.tgz", + "integrity": "sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==", + "requires": { + "lie": "3.1.1" + } + }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -8173,8 +8217,7 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "no-case": { "version": "2.3.2", diff --git a/package.json b/package.json index 4d09ca42..fd590d6b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "dependencies": { "ajv": "6.5.4", "chart.js": "2.7.2", + "cross-env": "^5.2.0", "highcharts": "^6.2.0", + "localforage": "^1.7.3", "lodash": "4.17.10", "moment": "2.22.1", "react": "16.3.2", @@ -20,7 +22,7 @@ "semantic-ui-react": "0.75.1" }, "scripts": { - "start": "react-scripts start", + "start": "cross-env NODE_ENV=development; react-scripts start", "build": "npm run docs && react-scripts build && node postbuild.js", "test": "react-scripts test", "coverage": "react-scripts test --coverage", diff --git a/src/App.js b/src/App.js index c2451e54..e95b7e2b 100644 --- a/src/App.js +++ b/src/App.js @@ -5,6 +5,7 @@ import { Switch, } from 'react-router-dom'; import { Helmet } from 'react-helmet'; +import localforage from 'localforage'; import { Confirmer } from './utils/getUserConfirmation'; @@ -18,14 +19,30 @@ import Header from './components/Header'; // Development HUD import { DevSwitch } from './containers/DevSwitch'; import { DevHud } from './components/dev/DevHud'; +import { + printSummaryToConsole, + addClientGetterProperty, + addEnableDevProperty, +} from './dev/command-line-utils'; // Object Manipulation -import { cloneDeep } from 'lodash'; +import cloneDeep from 'lodash/cloneDeep'; +import merge from 'lodash/merge'; import { CLIENT_DEFAULTS } from './utils/CLIENT_DEFAULTS'; // LOCALIZATION import { getTextForLanguage } from './utils/getTextForLanguage'; + +const DEV_PROPS_STORAGE_KEY = 'cliffEffectsDevProps'; + +const LOADED_CLIENT_STORAGE_KEY = 'cliffEffects_loadedClient'; + +const CLIENT_LAST_LOADED_STORAGE_KEY = 'cliffEffects_clientLastLoaded'; + +// Time-to-live for stored client data, in milliseconds +const STORED_CLIENT_TTL = 1000 * 60 * 60 * 24; // 1 day + /** * Main top-level component of the app. Contains the router that controls access * to the {@link HomePage}, {@link VisitPage}, and {@link AboutPage}, as well @@ -48,14 +65,6 @@ class App extends Component { let defaults = cloneDeep(CLIENT_DEFAULTS); - // Development variables are the only things stored - let localDev = localStorage.getItem(`cliffEffectsDevProps`); - if (typeof localDev !== `string`) { - localDev = {}; - } else { - localDev = JSON.parse(localDev); - } - /** * React state. * @property {string} langCode - [ISO 639-1 code]{@link https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes} of currently selected language @@ -84,12 +93,66 @@ class App extends Component { english: true, nonEnglish: true, warningOff: true, - ...localDev, }, distrustConfirmed: false, }; }; // End constructor() + componentDidMount() { + // Webpack should remove this whole conditional when not built for development environment + if (process.env.NODE_ENV === 'development') { + Promise.all([ + localforage.getItem(DEV_PROPS_STORAGE_KEY), + localforage.getItem(CLIENT_LAST_LOADED_STORAGE_KEY), + localforage.getItem(LOADED_CLIENT_STORAGE_KEY), + ]).then( + ([ + localDev, + clientLastLoaded, + loadedClient, + ]) => { + if (localDev) { + this.setState((prevState) => { + const now = Date.now(); + + clientLastLoaded = clientLastLoaded || 0; + + let state = merge( + {}, + prevState, + { devProps: localDev } + ); + + // This will clear out a loaded client from local storage + // if it's been there too long--this is for security purposes, + // as the loaded client could potentially have sensitive client + // data and we want to minimize exposure of that info. + if (now - clientLastLoaded >= STORED_CLIENT_TTL) { + localforage.removeItem(LOADED_CLIENT_STORAGE_KEY); + localforage.removeItem(CLIENT_LAST_LOADED_STORAGE_KEY); + } + else { + state.clients.loaded = loadedClient; + } + + return state; + }); + } + } + ); + + printSummaryToConsole(); + + addEnableDevProperty(() => { + return this.setDev('dev', true); + }); + + addClientGetterProperty(() => { + return this.state.clients.loaded; + }); + } // End development environment conditional + } + /** * Set the human language of the app (i.e. the language in which the UI will * display text for users to read, NOT the coding language). @@ -115,9 +178,11 @@ class App extends Component { let props = prevState.devProps; if (props[ key ] !== value) { - let newProps = { ...props, [ key ]: value }; - localStorage.setItem(`cliffEffectsDevProps`, JSON.stringify(newProps)); + + if (process.env.NODE_ENV === 'development') { + localforage.setItem(DEV_PROPS_STORAGE_KEY, newProps); + } return { devProps: newProps }; } @@ -139,6 +204,11 @@ class App extends Component { defaults = cloneDeep(clients.default), newData = Object.assign(defaults, toLoad); + if (process.env.NODE_ENV === 'development') { + localforage.setItem(CLIENT_LAST_LOADED_STORAGE_KEY, Date.now()); + localforage.setItem(LOADED_CLIENT_STORAGE_KEY, newData); + } + return { clients: { ...clients, loaded: newData }}; }); }; // End loadClient() diff --git a/src/components/MainMenu.js b/src/components/MainMenu.js index e0c5d3dd..6f79016d 100644 --- a/src/components/MainMenu.js +++ b/src/components/MainMenu.js @@ -11,30 +11,30 @@ const MainMenu = function ({ translations }) { + size = { `large` }> + className = { `main-nav` } + to = { `/` }> { translations.i_homeNav } + className = { `main-nav` } + to = { `/about` }> { translations.i_aboutNav } { translations.i_githubNav } - + {/**/} {/**/} ); -}; // End MainMenu(<>) +}; // Ends export { MainMenu }; diff --git a/src/components/PageLayout.js b/src/components/PageLayout.js index 57e7a384..3b61bf8a 100644 --- a/src/components/PageLayout.js +++ b/src/components/PageLayout.js @@ -4,23 +4,26 @@ import { Segment, } from 'semantic-ui-react'; -const PageLayout = (props) => { + +const PageLayout = ({ children }) => { return (
+ verticalAlign = { `middle` }> - {props.children} + + { children } + + floated = { `right` } + width = { 6 } /> @@ -28,4 +31,5 @@ const PageLayout = (props) => { ); }; + export { PageLayout }; diff --git a/src/components/StepBar.js b/src/components/StepBar.js index e8940e8c..bfed2c80 100644 --- a/src/components/StepBar.js +++ b/src/components/StepBar.js @@ -3,25 +3,29 @@ import { Step } from 'semantic-ui-react'; import { STEP_VALS } from '../forms/STEP_VALS'; + const StepBar = ({ currentStepKey, goToStep, translations }) => { let cleanSteps = []; STEP_VALS.forEach((step, index) => { let newStep = { title: { content: translations[ `i_` + step.key ] }}; - newStep.active = step.key === currentStepKey; - newStep.onClick = (e) => { - goToStep({ key: step.key }); - }; - newStep.key = index; + + newStep.active = step.key === currentStepKey; + newStep.onClick = function (event) { goToStep({ key: step.key }); }; + newStep.key = index; + cleanSteps[ index ] = newStep; }); - return (); + return ( + + ); }; -export default StepBar; + +export { StepBar }; diff --git a/src/components/Surrounder.js b/src/components/Surrounder.js index addef967..712d49bf 100644 --- a/src/components/Surrounder.js +++ b/src/components/Surrounder.js @@ -6,26 +6,26 @@ const Surrounder = function ({ Top, Left, Right, Bottom, children }) { let contents = { top: null, left: null, right: null, bottom: null }; if (Top) { - contents.top = (
{ Top }
); + contents.top = (
{ Top }
); } if (Left) { - contents.left = (
{ Left }
); + contents.left = (
{ Left }
); } if (Right) { - contents.right = (
{ Right }
); + contents.right = (
{ Right }
); } if (Bottom) { - contents.bottom = (
{ Bottom }
); + contents.bottom = (
{ Bottom }
); } return ( -
+
{ contents.top } -
+
{ contents.left } -
+
{ children }
@@ -36,7 +36,7 @@ const Surrounder = function ({ Top, Left, Right, Bottom, children }) {
); -}; // End +}; // Ends export { Surrounder }; diff --git a/src/components/dev/DevHud.js b/src/components/dev/DevHud.js index ed9f05e5..0968d20d 100644 --- a/src/components/dev/DevHud.js +++ b/src/components/dev/DevHud.js @@ -40,7 +40,7 @@ const DevMenu = function ({ devProps, funcs, data, state }) { } - open={ this.state.modalOpen } - onClose={ this.toggleModalOpen } + trigger = { } + open = { this.state.modalOpen } + onClose = { this.toggleModalOpen } closeIcon> - - Localization Report - + + Localization Report +

This report indicates whether keys in the 'en' localization file are present in - your chosen localization. It will also list any keys in your chosen localization + your chosen localization. It will also list any keys in your chosen localization which should be removed. indicates a check passed. + name = { `check square` } + size = { `large` } + color = { `green` } />indicates a check passed. indicates a check failed and action is needed. + name = { `window close` } + size = { `large` } + color = { `red` } />indicates a check failed and action is needed.

@@ -252,7 +270,7 @@ class LocalizationReport extends Component { @@ -261,4 +279,8 @@ class LocalizationReport extends Component { } }; -export { LocalizationReport }; +export { + LocalizationReport, + ReportItem, + ReportList, +}; diff --git a/src/containers/VisitPage.js b/src/containers/VisitPage.js index 016ca1fd..a29ae3bb 100644 --- a/src/containers/VisitPage.js +++ b/src/containers/VisitPage.js @@ -9,6 +9,7 @@ import { Redirect } from 'react-router-dom'; import { setNestedProperty } from '../utils/setNestedProperty'; import { cloneDeep } from 'lodash'; import { convertForUpdate } from '../utils/convertForUpdate'; +import { addClientGetterProperty } from '../dev/command-line-utils'; // Data // import { clientList } from '../config/dummyClients'; @@ -23,7 +24,7 @@ import { FeedbackPrompt } from '../components/prompts/FeedbackPrompt'; import { FeedbackForm } from '../components/prompts/FeedbackForm'; import { FeedbackAnytime } from '../components/prompts/FeedbackAnytime'; import { PredictionsWarning } from '../components/prompts/PredictionsWarning'; -import StepBar from '../components/StepBar'; +import { StepBar } from '../components/StepBar'; import { BigButton } from '../forms/inputs'; import { ButtonReset } from '../forms/ButtonReset'; import { STEP_VALS } from '../forms/STEP_VALS'; @@ -56,6 +57,15 @@ class VisitPage extends Component { componentDidMount() { this.didMount = true; + + // Webpack should remove this whole conditional when not built for development environment + if (process.env.NODE_ENV === 'development') { + // Override property set in App.js, because that property + // doesn't get changed by updateClientValue() + addClientGetterProperty(() => { + return this.state.client; + }); + } } resetClientIfOk = (shouldReset) => { diff --git a/src/data/federal/2017/SNAPData.js b/src/data/federal/2017/SNAPData.js index 4afbafee..14fc21e1 100644 --- a/src/data/federal/2017/SNAPData.js +++ b/src/data/federal/2017/SNAPData.js @@ -1,10 +1,19 @@ -/* - * RESOURCES - * Derived from https://www.masslegalservices.org/content/online-snap-calculator - * http://www.mass.gov/eohhs/consumer/basic-needs/financial/program-eligibility-charts-and-tables.html - * http://www.mass.gov/eohhs/gov/departments/dta/program-eligibility-charts-and-tables.html +/** Data tables for MA SNAP calculations. + * + * RESOURCES: + * Derived from {@link https://www.masslegalservices.org/content/online-snap-calculator} + * {@link http://www.mass.gov/eohhs/consumer/basic-needs/financial/program-eligibility-charts-and-tables.html} + * {@link http://www.mass.gov/eohhs/gov/departments/dta/program-eligibility-charts-and-tables.html} + * Standard deductions: {@link http://www.mass.gov/eohhs/docs/dta/eligibility-charts/c-snap-364-400.pdf} + * Utility allowance categories: {@link http://www.mass.gov/eohhs/docs/dta/eligibility-charts/c-snap-364-945.pdf} + * Net income limits: {@link http://www.mass.gov/eohhs/docs/dta/eligibility-charts/c-snap-364-970.pdf} + * SNAP allowance maximums: {@link http://www.mass.gov/eohhs/docs/dta/eligibility-charts/c-snap-364-980-hh-1-10.pdf} + * + * @namespace */ const SNAPData = { + // Code-style note: Properties have to use quotes + // in monthly values, because that is what 106 CMR 364.400 leads to: http://www.mass.gov/eohhs/docs/dta/eligibility-charts/c-snap-364-400.pdf STANDARD_DEDUCTIONS: { 0: 0, 1: 160, 2: 160, 3: 160, 4: 170, 5: 199, 6: 228, 'eachAdditional': 0 }, PERCENT_GROSS_MONTHLY_EARNED: 0.20, @@ -25,5 +34,7 @@ const SNAPData = { SMALL_HOUSEHOLD_SIZE: 2, SMALL_HOUSEHOLD_MIN_GRANT: 15, }; +// @todo: Make class for 'money amount by number of items'-type object + export { SNAPData }; diff --git a/src/data/federal/federalPovertyGuidelines.js b/src/data/federal/federalPovertyGuidelines.js index 22ac39cf..05db39f0 100644 --- a/src/data/federal/federalPovertyGuidelines.js +++ b/src/data/federal/federalPovertyGuidelines.js @@ -1,6 +1,12 @@ +/** Data tables for Federal poverty guildelines/income limits. + * + * RESOURCES: + * {@link https://aspe.hhs.gov/poverty-guidelines} + * {@link http://www.mass.gov/eohhs/docs/masshealth/deskguides/fpl-deskguide.pdf} + * + * @namespace + */ const federalPovertyGuidelines = { 0: 0, 1: 12060, 2: 16240, 3: 20420, 4: 24600, 5: 28780, 6: 32960, 7: 37140, 8: 41320, 'eachAdditional': 4180 }; -export { federalPovertyGuidelines }; -//http://www.mass.gov/eohhs/docs/masshealth/deskguides/fpl-deskguide.pdf -//https://aspe.hhs.gov/poverty-guidelines +export { federalPovertyGuidelines }; diff --git a/src/data/massachusetts/name-cores.js b/src/data/massachusetts/name-cores.js index 1ecd8014..d27674b1 100644 --- a/src/data/massachusetts/name-cores.js +++ b/src/data/massachusetts/name-cores.js @@ -1,36 +1,44 @@ -/** Arrays of names which can act as a base off of which -* to build client property keys +/** Collections of names that fall under common MA categories. +* Right now they're meant to be used to make sums of +* client property values. This has been useful when +* property names have changed. +* @module * -* @example var gross = client[ timeframe + base + 'Monthly' ]; +* @example +* import { sumProps } from './someUtilityFolder'; +* let client = { disabledMedical: 1, otherMedical: 10, disabledAssistance: 100 }; +* var allMedicalExpenses = sumProps(ALL_MEDICAL_EXPENSES); +* console.log(allMedicalExpenses); +* // 111 */ const UNEARNED_INCOME_SOURCES = [ - 'TAFDC', - 'SSI', - 'SSDI', - 'childSupportIn', - 'unemployment', - 'workersComp', - 'pension', - 'socialSecurity', - 'alimony', - 'otherIncome', + `TAFDC`, + `SSI`, + `SSDI`, + `childSupportIn`, + `unemployment`, + `workersComp`, + `pension`, + `socialSecurity`, + `alimony`, + `otherIncome`, ]; const UNDER13_CARE_EXPENSES = [ - 'childDirectCare', - 'childBeforeAndAfterSchoolCare', - 'childTransportation', - 'childOtherCare', + `childDirectCare`, + `childBeforeAndAfterSchoolCare`, + `childTransportation`, + `childOtherCare`, ]; const OVER12_CARE_EXPENSES = [ - 'adultDirectCare', - 'adultTransportation', - 'adultOtherCare', + `adultDirectCare`, + `adultTransportation`, + `adultOtherCare`, ]; -/** @todo Convert all to this later, but will have +/* @todo Convert all to this later, but will have * to generate new SNAP test cases because the * strings they check against have the array * elements in a different order. @@ -40,14 +48,14 @@ const UNDER13_NON_TRANSPORT_CARE = [ `childBeforeAndAfterSchoolCare`, `childOtherCare`, ]; -const UNDER13_TRANSPORT = [ `childTransportation` ]; +const UNDER13_TRANSPORT = [ `childTransportation` ]; // const UNDER13_CARE_EXPENSES = UNDER13_NON_TRANSPORT_CARE.concat(UNDER13_TRANSPORT); const OVER12_NON_TRANSPORT_CARE = [ `adultDirectCare`, `adultOtherCare`, ]; -const OVER12_TRANSPORT = [ `adultTransportation` ]; +const OVER12_TRANSPORT = [ `adultTransportation` ]; // const OVER12_CARE_EXPENSES = OVER12_NON_TRANSPORT_CARE.concat(OVER12_TRANSPORT); const NON_TRANSPORT_DEPENDENT_COSTS = UNDER13_NON_TRANSPORT_CARE.concat(OVER12_NON_TRANSPORT_CARE); diff --git a/src/dev/command-line-utils.js b/src/dev/command-line-utils.js new file mode 100644 index 00000000..a5f4565c --- /dev/null +++ b/src/dev/command-line-utils.js @@ -0,0 +1,43 @@ +import cloneDeep from 'lodash/cloneDeep'; + +export const printSummaryToConsole = () => { + console.log( + ` +%c========================= Cliff Effects Dev Utilities ========================= + +- enableDev() - Enable the Dev HUD (without needing to go to /dev) + +- clientClone - A deep clone of the current value of the loaded client + +===============================================================================`, + 'color: blue', + ); +}; + +export const addEnableDevProperty = (devSetter) => { + Object.defineProperties( + window, + { + enableDev: { + enumerable: true, + configurable: true, + value: devSetter, + }, + } + ); +}; + +export const addClientGetterProperty = (clientGetter) => { + Object.defineProperties( + window, + { + clientClone: { + enumerable: true, + configurable: true, + get: () => { + return cloneDeep(clientGetter()); + }, + }, + } + ); +}; diff --git a/src/forms/output/BenefitsLineGraph.js b/src/forms/output/BenefitsLineGraph.js deleted file mode 100644 index daa0bb33..00000000 --- a/src/forms/output/BenefitsLineGraph.js +++ /dev/null @@ -1,124 +0,0 @@ -import React, { Component } from 'react'; -import _ from 'lodash'; -import { Line } from 'react-chartjs-2'; -import { Message } from 'semantic-ui-react'; - -// CUSTOM ELEMENTS -import { VerticalLine } from './VerticalLine'; - -// LOGIC -import { timescaleMultipliers } from '../../utils/convert-by-timescale'; -import { - formatAxis, - formatLabel, - formatBenefitLinesTitle, -} from '../../utils/charts/chartFormatting'; -import { getChartData } from '../../utils/charts/getChartData'; - -// DATA -// In future, graphs will control their own aspect ratio, -// zoom levels, etc., so for now they'll have access to -// the limit values. -import { PROGRAM_CHART_VALUES } from '../../utils/charts/PROGRAM_CHART_VALUES'; - - -// Graphs get things in monthly values, so we'll convert from there -let multipliers = timescaleMultipliers.fromMonthly, - // Each graph controls its own scaling - limits = PROGRAM_CHART_VALUES.limits; - - -class BenefitsLineGraph extends Component { - - constructor (props) { - super(props); - this.state = { verticalLine: new VerticalLine() }; - } - - render () { - const { client, timescale, activePrograms, className } = this.props; - const multiplier = multipliers[ timescale ]; - - if (activePrograms.length === 0) { - return No public benefit programs have been selected; - } - - // Adjust to time-interval, round to hundreds - let max = Math.ceil((limits.max * multiplier) / 100) * 100, - interval = Math.ceil((max / 100) / 10) * 10; - - let extraProps = {}; - - for (let benefitIndex = 0; benefitIndex < activePrograms.length; benefitIndex++) { - extraProps[ activePrograms[ benefitIndex ] ] = { fill: false }; - } - - const xRange = _.range(limits.min, max, interval), // x-axis/earned income numbers - datasets = getChartData(xRange, multiplier, client, activePrograms, extraProps); - - // If there's no data to show, don't show the table - if (datasets.length === 0) { - return null; - } - - // react-chartjs-2 keeps references to plugins, so we - // have to mutate that reference - const earned = client.future.earned * multiplier, - hack = this.state.verticalLine; - hack.xRange = xRange; - hack.earned = earned; - - let lineProps = { - data: { - labels: xRange, - datasets: datasets, - }, // end `data` - options: { - title: { - display: true, - text: 'Individual Benefit Amounts for Household as Pay Changes', - }, - showLines: true, - scales: { - yAxes: [ - { - scaleLabel: { - display: true, - labelString: 'Benefit Value ($)', - }, - ticks: { - beginAtZero: true, - /* chart.js v2.7 requires a callback function */ - callback: formatAxis, - }, - }, - ], // end `yAxes` - xAxes: [ - { - scaleLabel: { - display: true, - labelString: timescale + ' Pay ($)', - }, - ticks: { callback: formatAxis }, - }, - ], // end `xAxes` - }, // end `scales` - tooltips: { - callbacks: { - title: formatBenefitLinesTitle, - label: formatLabel, - }, - }, // end `tooltips` - }, // end `options` - plugins: [ this.state.verticalLine ], - }; // end lineProps - - return ( - - ); - } - -}; // End - - -export { BenefitsLineGraph }; diff --git a/src/forms/output/BenefitsLines.js b/src/forms/output/BenefitsLines.js index 68f654d4..3333348d 100644 --- a/src/forms/output/BenefitsLines.js +++ b/src/forms/output/BenefitsLines.js @@ -81,21 +81,21 @@ class BenefitsLinesComp extends Component { classes += ` ` + className; } - const multiplier = multipliers[ timescale ], - resources = activePrograms, - currentEarned = client.current.earned * multiplier, - getText = textFromTranslatedElement; + let multiplier = multipliers[ timescale ], + resources = activePrograms, + currentEarned = client.current.earned * multiplier, + getText = textFromTranslatedElement; // Adjust to time-interval. Highcharts will round // for displayed ticks. - const max = (limits.max * multiplier), - interval = ((max / 100) / 10); + let max = (limits.max * multiplier), + interval = ((max / 100) / 10); - const xRange = range(limits.min, max, interval), // x-axis/earned income numbers - datasets = getChartData(xRange, multiplier, client, resources, {}); + let xRange = range(limits.min, max, interval), // x-axis/earned income numbers + datasets = getChartData(xRange, multiplier, client, resources, {}); // Individual benefit lines - const lines = []; + let lines = []; for (let dataset of datasets) { let line = ( ${getText(translations.i_beforeMoney)}`, - labelHeaderFormatEnd = `{point.key:,.2f}${getText(translations.i_afterMoney)}
`, - labelHeaderFormat = labelHeaderFormatStart + labelHeaderFormatEnd; + let labelHeaderFormatStart = `${getText(translations.i_beforeMoney)}`, + labelHeaderFormatEnd = `{point.key:,.2f}${getText(translations.i_afterMoney)}
`, + labelHeaderFormat = labelHeaderFormatStart + labelHeaderFormatEnd; - const plotOptions = { line: { pointInterval: interval }}; + let plotOptions = { line: { pointInterval: interval }}; return (
@@ -265,10 +265,10 @@ class BenefitsLinesComp extends Component { this.setState({ altKeyClass: `` }); } }; -}; +}; // Ends -const BenefitsLines = withHighcharts(BenefitsLinesComp, Highcharts); +let BenefitsLines = withHighcharts(BenefitsLinesComp, Highcharts); export { BenefitsLines }; diff --git a/src/forms/output/BenefitsTable.js b/src/forms/output/BenefitsTable.js index eb2e81a6..c9052400 100644 --- a/src/forms/output/BenefitsTable.js +++ b/src/forms/output/BenefitsTable.js @@ -9,15 +9,14 @@ import { applyAndPushBenefits } from '../../programs/applyAndPushBenefits'; import { cloneDeep } from 'lodash'; +// @todo Move to utils file somewhere? const getSignSymbol = function (num) { if (num > 0) { - return '+'; - } - else if (num < 0) { - return '-'; - } - else { return ''; } -}; // End getSignSymbol() + return `+`; + } else if (num < 0) { + return `-`; + } else { return ``; } +}; const BenefitsTable = function ({ client, translations }) { @@ -49,77 +48,75 @@ const BenefitsTable = function ({ client, translations }) { }; applyAndPushBenefits (futureCalcData); - const earned = allData.earned; + let earned = allData.earned; - const currentBenefits = {}; - const futureBenefits = {}; - const benefitDiffs = {}; - let totalDiff = 0; - let totalBenefitCurrent = 0; - let totalBenefitFuture = 0; + let currentBenefits = {}, + futureBenefits = {}, + benefitDiffs = {}, + totalDiff = 0, + totalBenefitCurrent = 0, + totalBenefitFuture = 0; for (let benefitIndex = 0; benefitIndex < curr.benefits.length; benefitIndex++) { - const benefit = curr.benefits[ benefitIndex ]; - - const benefitData = allData[ benefit ]; + + let benefit = curr.benefits[ benefitIndex ], + benefitData = allData[ benefit ]; if (benefitData) { - const [ + let [ currentBenefit, futureBenefit, ] = benefitData; currentBenefits[ benefit ] = Math.round(currentBenefit); - - futureBenefits[ benefit ] = Math.round(futureBenefit); + futureBenefits[ benefit ] = Math.round(futureBenefit); totalBenefitCurrent += currentBenefits[ benefit ]; - totalBenefitFuture += futureBenefits[ benefit ]; - } - else { + totalBenefitFuture += futureBenefits[ benefit ]; + } else { currentBenefits[ benefit ] = 0; - - futureBenefits[ benefit ] = 0; + futureBenefits[ benefit ] = 0; } benefitDiffs[ benefit ] = futureBenefits[ benefit ] - currentBenefits[ benefit ]; totalDiff += benefitDiffs[ benefit ]; } - const earnedCurrent = Math.round(earned[ 0 ]), - earnedFuture = Math.round(earned[ 1 ]), - earnedDiff = earnedFuture - earnedCurrent, - netCurrent = totalBenefitCurrent + earnedCurrent, - netFuture = totalBenefitFuture + earnedFuture, - netDiff = totalDiff + earnedDiff; - - const columnHeaderStyle = { - background: 'rgba(0, 181, 173, 1)', - color: 'white', - fontSize: '1.3em', - fontWeight: 900, - textAlign: 'center', - borderRadius: 'inherit', - letterSpacing: '0.02em', - }, - totalsRowStyle = { - borderTop: '2px solid rgba(0, 181, 173, 1)', - fontWeight: 700, - fontSize: '1.1em', - padingTop: '0.25em', - }, - rowHeaderStyle = { - fontSize: '1.1em', - fontWeight: 700, - textAlign: 'left', - }, - totalsRowHeaderStyle = { - fontSize: '1.2em', - fontWeight: 700, - textAlign: 'left', - borderTop: '2px solid rgba(0, 181, 173, 1)', - padingTop: '0.25em', - }; + let earnedCurrent = Math.round(earned[ 0 ]), + earnedFuture = Math.round(earned[ 1 ]), + earnedDiff = earnedFuture - earnedCurrent, + netCurrent = totalBenefitCurrent + earnedCurrent, + netFuture = totalBenefitFuture + earnedFuture, + netDiff = totalDiff + earnedDiff; + + // @todo Possible to break the following components out? + let columnHeaderStyle = { + background: `rgba(0, 181, 173, 1)`, + color: `white`, + fontSize: `1.3em`, + fontWeight: 900, + textAlign: `center`, + borderRadius: `inherit`, + letterSpacing: `0.02em`, + }, + totalsRowStyle = { + borderTop: `2px solid rgba(0, 181, 173, 1)`, + fontWeight: 700, + fontSize: `1.1em`, + padingTop: `0.25em`, + }, + rowHeaderStyle = { + fontSize: `1.1em`, + fontWeight: 700, + textAlign: `left`, + }, + totalsRowHeaderStyle = { + fontSize: `1.2em`, + fontWeight: 700, + textAlign: `left`, + borderTop: `2px solid rgba(0, 181, 173, 1)`, + padingTop: `0.25em`, + }; const TotalBenefitsRow = function({ client, translations }){ if (client.current.benefits.length <= 1) { @@ -129,24 +126,28 @@ const BenefitsTable = function ({ client, translations }) { return ( { translations.i_rowTotalBenefits } + textAlign = { `right` } + width = { 3 } + style = { totalsRowHeaderStyle }> + { translations.i_rowTotalBenefits } { translations.i_beforeMoneyWithTime }{totalBenefitCurrent}{ translations.i_afterMoneyWithTime } + textAlign = { `right` } + width = { 3 } + style = { totalsRowStyle }> + { translations.i_beforeMoneyWithTime }{totalBenefitCurrent}{ translations.i_afterMoneyWithTime } { translations.i_beforeMoneyWithTime }{totalBenefitFuture}{ translations.i_afterMoneyWithTime } + textAlign = { `right` } + width = { 3 } + style = { totalsRowStyle }> + { translations.i_beforeMoneyWithTime }{totalBenefitFuture}{ translations.i_afterMoneyWithTime } { getSignSymbol(totalDiff) } { translations.i_beforeMoneyWithTime }{ Math.abs(totalDiff) }{ translations.i_afterMoneyWithTime } + textAlign = { `right` } + width = { 3 } + style = { totalsRowStyle }> + { getSignSymbol(totalDiff) } { translations.i_beforeMoneyWithTime }{ Math.abs(totalDiff) }{ translations.i_afterMoneyWithTime } ); @@ -156,35 +157,39 @@ const BenefitsTable = function ({ client, translations }) { return ( { translations.i_rowEarned } - { translations.i_beforeMoneyWithTime }{earnedCurrent}{ translations.i_afterMoneyWithTime } - { translations.i_beforeMoneyWithTime }{earnedFuture}{ translations.i_afterMoneyWithTime } - { getSignSymbol(earnedDiff) } { translations.i_beforeMoneyWithTime }{ Math.abs(earnedDiff) }{ translations.i_afterMoneyWithTime } + { translations.i_beforeMoneyWithTime }{earnedCurrent}{ translations.i_afterMoneyWithTime } + { translations.i_beforeMoneyWithTime }{earnedFuture}{ translations.i_afterMoneyWithTime } + { getSignSymbol(earnedDiff) } { translations.i_beforeMoneyWithTime }{ Math.abs(earnedDiff) }{ translations.i_afterMoneyWithTime } ); }; const TotalsRow = function ({ translations }) { return ( - + { translations.i_rowNetTotal } + textAlign = { `right` } + width = { 3 } + style = { totalsRowHeaderStyle }> + { translations.i_rowNetTotal } { translations.i_beforeMoneyWithTime }{netCurrent}{ translations.i_afterMoneyWithTime } + textAlign = { `right` } + width = { 3 } + style = { totalsRowStyle }> + { translations.i_beforeMoneyWithTime }{netCurrent}{ translations.i_afterMoneyWithTime } { translations.i_beforeMoneyWithTime }{netFuture}{ translations.i_afterMoneyWithTime } + textAlign = { `right` } + width = { 3 } + style = { totalsRowStyle }> + { translations.i_beforeMoneyWithTime }{netFuture}{ translations.i_afterMoneyWithTime } { getSignSymbol(netDiff) } { translations.i_beforeMoneyWithTime }{ Math.abs(netDiff) }{ translations.i_afterMoneyWithTime } + textAlign = { `right` } + width = { 3 } + style = { totalsRowStyle }> + { getSignSymbol(netDiff) } { translations.i_beforeMoneyWithTime }{ Math.abs(netDiff) }{ translations.i_afterMoneyWithTime } ); @@ -193,59 +198,60 @@ const BenefitsTable = function ({ client, translations }) { const benefitRows = []; for (let benefitIndex = 0; benefitIndex < curr.benefits.length; benefitIndex++) { - const benefit = curr.benefits[ benefitIndex ]; - - const diff = benefitDiffs[ benefit ]; - - const label = translations[ `i_row_${benefit}` ]; + let benefit = curr.benefits[ benefitIndex ], + diff = benefitDiffs[ benefit ], + label = translations[ `i_row_${benefit}` ]; benefitRows.push( { label } - { translations.i_beforeMoneyWithTime }{currentBenefits[ benefit ]}{ translations.i_afterMoneyWithTime } - { translations.i_beforeMoneyWithTime }{futureBenefits[ benefit ]}{ translations.i_afterMoneyWithTime } - { getSignSymbol(diff) } { translations.i_beforeMoneyWithTime }{ Math.abs(diff) }{ translations.i_afterMoneyWithTime } + { translations.i_beforeMoneyWithTime }{currentBenefits[ benefit ]}{ translations.i_afterMoneyWithTime } + { translations.i_beforeMoneyWithTime }{futureBenefits[ benefit ]}{ translations.i_afterMoneyWithTime } + { getSignSymbol(diff) } { translations.i_beforeMoneyWithTime }{ Math.abs(diff) }{ translations.i_afterMoneyWithTime } ); } return ( -
- - - - { translations.i_columnBenefit } - - { translations.i_columnCurrentBenefits } - - { translations.i_columnNewEstimate } - - { translations.i_columnDifference } - - - - - {benefitRows} - - - - -
-
+ + + + + { translations.i_columnBenefit } + + + { translations.i_columnCurrentBenefits } + + + { translations.i_columnNewEstimate } + + + { translations.i_columnDifference } + + + + + + { benefitRows } + + + + +
); -}; // End BenefitsTable(<>) +}; // Ends export { BenefitsTable }; diff --git a/src/forms/output/GraphHolder.js b/src/forms/output/GraphHolder.js index 9e757368..31771c6d 100644 --- a/src/forms/output/GraphHolder.js +++ b/src/forms/output/GraphHolder.js @@ -5,11 +5,14 @@ import { Message } from 'semantic-ui-react'; import { GraphTimeButtons } from '../../components/GraphTimeButtons'; +/** Keeps track of time interval currently requested + * (weekly/monthly/yearly) and, if needed, shows + * 'no graph' message. */ class GraphHolder extends Component { constructor (props) { super(props); - this.state = { activeID: 'Monthly' }; + this.state = { activeID: `Monthly` }; } onClick = (evnt) => { @@ -18,33 +21,32 @@ class GraphHolder extends Component { }; render () { - const { activeID } = this.state, + const { activeID } = this.state, { Graph, client, translations } = this.props, - { current } = client, + { current } = client, // The ids later used to access all program-specific data and functions // Only active programs are added - activePrograms = [ ...current.benefits ]; + activePrograms = [ ...current.benefits ]; if (activePrograms.length === 0) { return { translations.i_noBenefitsSelected }; } return ( -
+
+ activeID = { activeID } + onClick = { this.onClick } /> + translations = { translations } />
); - }; // End render() - -}; // End + }; // Ends render() +}; // Ends export { GraphHolder }; diff --git a/src/forms/output/ResourcesColumns.js b/src/forms/output/ResourcesColumns.js index 7ba3fe5c..7aae30fa 100644 --- a/src/forms/output/ResourcesColumns.js +++ b/src/forms/output/ResourcesColumns.js @@ -68,11 +68,11 @@ class ResourcesColumnsComp extends Component { // Adjust to time-interval. Highcharts will round // for displayed ticks. - const xRange = [ - currentEarned, - futureEarned, - ], // x-axis/earned income numbers - datasets = getChartData(xRange, multiplier, clone, resources, {}); + let xRange = [ + currentEarned, + futureEarned, + ], // x-axis/earned income numbers + datasets = getChartData(xRange, multiplier, clone, resources, {}); // Columns and categories for each pay amount let columns = [], @@ -98,7 +98,7 @@ class ResourcesColumnsComp extends Component { categories.push(formatted); } } - } + } // ends for every dataset const plotOptions = { column: { stacking: `normal` }}; @@ -108,8 +108,8 @@ class ResourcesColumnsComp extends Component { { getText(translations.i_stackedBarGraphTitle) } @@ -146,10 +146,9 @@ class ResourcesColumnsComp extends Component { -
); - } // Ends render() + }; // Ends render() /** Adds translation-specific money designations @@ -174,7 +173,7 @@ class ResourcesColumnsComp extends Component { formatMoneyWithK = (highchartsObject) => { return formatMoneyWithK(highchartsObject, this.props.translations); }; -}; +}; // Ends const ResourcesColumns = withHighcharts(ResourcesColumnsComp, Highcharts); diff --git a/src/forms/output/StackedAreaGraph.js b/src/forms/output/StackedAreaGraph.js deleted file mode 100644 index 707e4f85..00000000 --- a/src/forms/output/StackedAreaGraph.js +++ /dev/null @@ -1,130 +0,0 @@ -import React, { Component } from 'react'; -import _ from 'lodash'; -import { Line } from 'react-chartjs-2'; - -// CUSTOM ELEMENTS -import { VerticalLine } from './VerticalLine'; - -// LOGIC -import { timescaleMultipliers } from '../../utils/convert-by-timescale'; -import { - formatAxis, - formatLabel, - formatStackedTitle, -} from '../../utils/charts/chartFormatting'; -import { getChartData } from '../../utils/charts/getChartData'; - -// DATA -// In future, graphs will control their own aspect ratio, -// zoom levels, etc., so for now they'll have access to -// the limit values. -import { PROGRAM_CHART_VALUES } from '../../utils/charts/PROGRAM_CHART_VALUES'; - - -// Graphs get things in monthly values, so we'll convert from there -let multipliers = timescaleMultipliers.fromMonthly, - // Each graph controls its own scaling - limits = PROGRAM_CHART_VALUES.limits; - -// =============== -// GRAPH DATA -// =============== -/* Note: default tooltip for chart.js 2.0+: - * options: { tooltips: { callbacks: { - * label: function(tooltipItem, data) { - * return tooltipItem.yLabel; - * } - * }}} - */ -class StackedAreaGraph extends Component { - - constructor (props) { - super(props); - this.state = { verticalLine: new VerticalLine() }; - } - - render () { - const { client, timescale, activePrograms } = this.props; - const multiplier = multipliers[ timescale ]; - - let withEarned = activePrograms.slice(); - withEarned.unshift('earned'); - - // Adjust to time-interval, round to hundreds - const earned = client.future.earned * multiplier, - max = Math.max(earned, limits.max * multiplier), - xMax = Math.ceil(max / 100) * 100, - xMin = Math.ceil(limits.min * multiplier / 100) * 100, - interval = Math.ceil(((xMax - xMin) / 100) / 10) * 10, - xRange = _.range(xMin, xMax + interval, interval), - extraProps = { earned: { fill: 'origin' }}, - datasets = getChartData(xRange, multiplier, client, withEarned, extraProps); - - // react-chartjs-2 keeps references to plugins, so we - // have to mutate that reference - let hack = this.state.verticalLine; - hack.xRange = xRange; - hack.earned = earned; - - let stackedAreaProps = { - data: { - labels: xRange, - datasets: datasets, - }, // end `data` - options: { - title: { - display: true, - text: 'All Money Coming in as Pay Changes', - }, // end `title` - elements: { - line: { fill: '-1' }, - point: { - radius: 0, - hitRadius: 10, - hoverRadius: 10, - }, - }, // end `elements` - scales: { - yAxes: [ - { - stacked: true, - scaleLabel: { - display: true, - labelString: 'Total Money Coming In ($)', - }, - ticks: { - beginAtZero: true, - callback: formatAxis, - }, - }, - ], // end `yAxes` - xAxes: [ - { - stacked: true, - scaleLabel: { - display: true, - labelString: timescale + ' Earned ($)', - }, - ticks: { callback: formatAxis }, - }, - ], // end `xAxes` - }, // end `scales` - tooltips: { - callbacks: { - title: formatStackedTitle, - label: formatLabel, - }, - }, // end `tooltips` - }, // end `options` - plugins: [ this.state.verticalLine ], - redraw: true, - }; // end `stackedAreaProps` - - return ( - - ); - } -}; // End - - -export { StackedAreaGraph }; diff --git a/src/forms/output/StackedBarGraph.js b/src/forms/output/StackedBarGraph.js deleted file mode 100644 index 754751c0..00000000 --- a/src/forms/output/StackedBarGraph.js +++ /dev/null @@ -1,130 +0,0 @@ -import React from 'react'; -import { Bar } from 'react-chartjs-2'; - -// COMPONENT HELPER FUNCTIONS -import { - formatAxis, - formatLabel, - formatStackedTitle, -} from '../../utils/charts/chartFormatting'; -import { applyAndPushBenefits } from '../../programs/applyAndPushBenefits'; - -// DATA -// Colors and text for parts of the chart -import { PROGRAM_CHART_VALUES } from '../../utils/charts/PROGRAM_CHART_VALUES'; - -// OBJECT MANIPULATION -import { cloneDeep } from 'lodash'; - - -/** Visual representation of the table - * - * Alternatives: - * 1. Stacked area graph for two data points - * 2. Line graph of difference of just total money coming in - * - * @param {object} client Full client object, with current - * and future. All client props are needed. - */ -const StackedBarGraph = function({ client }) { - - let clone = cloneDeep(client), - curr = clone.current; - - const allData = {}, - activeBenefits = [ - `earned`, - ...curr.benefits, - ]; - - let currentCalcData = { - activeBenefits: activeBenefits, - dataToAddTo: allData, - clientToChange: clone, - timeframe: `current`, - }; - applyAndPushBenefits (currentCalcData); - - // Add to the `current` data already there - let futureCalcData = { - activeBenefits: activeBenefits, - dataToAddTo: allData, - clientToChange: clone, - timeframe: `future`, - }; - applyAndPushBenefits (futureCalcData); - - let datasets = [], - moneyLabels = []; - for (let bName of activeBenefits) { - - let frosting = PROGRAM_CHART_VALUES[ bName ], - dataset = { - label: frosting.name, - backgroundColor: frosting.color, - data: allData[ bName ], - }; - - if (bName === `earned`) { - dataset.fill = `origin`; - for (let amount of dataset.data) { - moneyLabels.push(Math.round(amount)); - } - } - - datasets.push(dataset); - } // end for each benefit in order - - const stackedBarProps = { - data: { - labels: moneyLabels, - datasets: datasets, - }, - options: { - title: { - display: true, - text: 'Money Coming In as Pay Changes', - }, - scales: { - yAxes: [ - { - stacked: true, - scaleLabel: { - display: true, - labelString: 'Total Money Coming In ($)', - }, - ticks: { - beginAtZero: true, - callback: formatAxis, - }, - }, - ], - xAxes: [ - { - stacked: true, - scaleLabel: { - display: true, - labelString: 'Monthly Pay ($)', - }, - ticks: { callback: formatAxis }, - }, - ], - }, - tooltips: { - callbacks: { - title: formatStackedTitle, - label: formatLabel, - }, - }, - }, - }; - - - return ( - - ); - -}; // End - - -export { StackedBarGraph }; diff --git a/src/forms/output/StackedResources.js b/src/forms/output/StackedResources.js index e569107c..99af73b1 100644 --- a/src/forms/output/StackedResources.js +++ b/src/forms/output/StackedResources.js @@ -86,21 +86,21 @@ class StackedResourcesComp extends Component { classes += ` ` + className; } - const multiplier = multipliers[ timescale ], - resources = [ `earned` ].concat(activePrograms), - currentEarned = client.current.earned * multiplier, - getText = textFromTranslatedElement; + let multiplier = multipliers[ timescale ], + resources = [ `earned` ].concat(activePrograms), + currentEarned = client.current.earned * multiplier, + getText = textFromTranslatedElement; // Adjust to time-interval. Highcharts will round // for displayed ticks. - const max = (limits.max * multiplier), - interval = ((max / 100) / 10); + let max = (limits.max * multiplier), + interval = ((max / 100) / 10); - const xRange = range(limits.min, max, interval), // x-axis/earned income numbers - datasets = getChartData(xRange, multiplier, client, resources, {}); + let xRange = range(limits.min, max, interval), // x-axis/earned income numbers + datasets = getChartData(xRange, multiplier, client, resources, {}); // Data to stack - const lines = []; + let lines = []; for (let dataseti = 0; dataseti < datasets.length; dataseti++) { let dataset = datasets[ dataseti ], line = ( @@ -119,12 +119,12 @@ class StackedResourcesComp extends Component { // Label for split tooltip 'labels'/'label headers' that appear // at the bottom. Really long. // @todo Change to prep for context, like in @knod 'other-expenses' branch - const bottomTooltipFormatStart = `${getText(translations.i_beforeMoney)}`, - bottomTooltipFormatEnd = `{point.key:,.2f}${getText(translations.i_afterMoney)}
`, - bottomTooltipFormat = bottomTooltipFormatStart + bottomTooltipFormatEnd; + let bottomTooltipFormatStart = `${getText(translations.i_beforeMoney)}`, + bottomTooltipFormatEnd = `{point.key:,.2f}${getText(translations.i_afterMoney)}
`, + bottomTooltipFormat = bottomTooltipFormatStart + bottomTooltipFormatEnd; - const plotOptions = { + let plotOptions = { area: { stacking: `normal`, pointInterval: interval }, series: { marker: { enabled: false }}, // No dots on the lines }; @@ -277,10 +277,10 @@ class StackedResourcesComp extends Component { this.setState({ altKeyClass: `` }); } }; -}; +}; // Ends -const StackedResources = withHighcharts(StackedResourcesComp, Highcharts); +let StackedResources = withHighcharts(StackedResourcesComp, Highcharts); export { StackedResources }; diff --git a/src/forms/output/Summary.js b/src/forms/output/Summary.js index 331586af..44e0ea7c 100644 --- a/src/forms/output/Summary.js +++ b/src/forms/output/Summary.js @@ -1,6 +1,11 @@ +/** @module */ + // REACT COMPONENTS import React from 'react'; -import { Header, Button } from 'semantic-ui-react'; +import { + Header, + Button, +} from 'semantic-ui-react'; // DATA // Colors and text for parts of the chart @@ -14,7 +19,7 @@ import { toMoneyStr } from '../../utils/prettifiers'; import { applyAndPushBenefits } from '../../programs/applyAndPushBenefits'; -let EARNED_MONTHLY_INCREMENT_AMOUNT = 50; // About a 25 cent raise in monthly amount for 40hrs/week? +const EARNED_MONTHLY_INCREMENT_AMOUNT = 50; // About a 25 cent raise in monthly amount for 40hrs/week? /** Rounds money values, turns them into money-formatted @@ -210,6 +215,7 @@ let getBenefitData = function(client, resourceKeys) { clientToChange: clone, timeframe: `current`, }; + let currentCalcData = defaultProps; applyAndPushBenefits(currentCalcData); let futureCalcData = { ...defaultProps, timeframe: `future` }; @@ -220,8 +226,8 @@ let getBenefitData = function(client, resourceKeys) { // Fill earned values for both current and future earned objects result.current = fillInMoneyValues(resourceKeys, accumulated, 0); result.future = fillInMoneyValues(resourceKeys, accumulated, 1); - let resultCurr = result.current, - resultFutr = result.future; + let resultCurr = result.current, + resultFutr = result.future; // 3. Get difference between totals, partly to // see if we need to get cliff info. @@ -245,7 +251,7 @@ let getBenefitData = function(client, resourceKeys) { result.recovery.total = recoveryAmount; result.recovery.earned = earned[ earned.length - 1 ]; - } // ends if hit dramatic cliff + } // ends if hit dramatic cliff (implicit taxes > 100%) return result; }; // Ends getBenefitData() @@ -328,7 +334,7 @@ const Summary = function ({ client, openFeedback, translations }) { fBenefit = future.benefits[ benefiti ]; benefitList.push( -
  • +
  • {cBenefit.label} {translations.i_from} {translations.i_beforeMoneyWithTime}{round$(cBenefit.amount)} {` `} {translations.i_to} {translations.i_beforeMoneyWithTime}{round$(fBenefit.amount)} {translations.i_eachTimeInterval}{translations.i_period}
  • @@ -340,9 +346,7 @@ const Summary = function ({ client, openFeedback, translations }) { // that the tool is still a prototype let feedbackAsk = (

    - - { translations.i_feedbackAsk } - + { translations.i_feedbackAsk }