From fefeca601f2b6020518914bec50daa5b4a8b0b34 Mon Sep 17 00:00:00 2001 From: Spruce Weber-Milne Date: Tue, 13 Dec 2022 10:19:03 -0800 Subject: [PATCH 1/5] Add tab for displaying ungrouped expenses --- src/AD419/ClientApp/src/App.tsx | 4 +- .../ClientApp/src/components/NavMenu.tsx | 5 + .../src/components/expenses/ExpenseTable.tsx | 160 ++++++++++++++++ .../components/expenses/ExpensesContainer.tsx | 171 ++++++++++++++++++ src/AD419/ClientApp/src/models/index.ts | 18 +- src/AD419/Controllers/ExpenseController.cs | 68 +++++++ 6 files changed, 424 insertions(+), 2 deletions(-) create mode 100644 src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx create mode 100644 src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx diff --git a/src/AD419/ClientApp/src/App.tsx b/src/AD419/ClientApp/src/App.tsx index e7680e8..2eade0b 100644 --- a/src/AD419/ClientApp/src/App.tsx +++ b/src/AD419/ClientApp/src/App.tsx @@ -4,9 +4,10 @@ import { Layout } from './components/Layout'; import AssociationContainer from './components/associations/AssociationContainer'; import SummaryContainer from './components/summary/SummaryContainer'; +import ExpensesContainer from './components/expenses/ExpensesContainer'; import Access from './Access'; -import './sass/custom.scss' +import './sass/custom.scss'; export default class App extends Component { static displayName = App.name; @@ -16,6 +17,7 @@ export default class App extends Component { + ); diff --git a/src/AD419/ClientApp/src/components/NavMenu.tsx b/src/AD419/ClientApp/src/components/NavMenu.tsx index b5395c4..7ef0ba8 100644 --- a/src/AD419/ClientApp/src/components/NavMenu.tsx +++ b/src/AD419/ClientApp/src/components/NavMenu.tsx @@ -47,6 +47,11 @@ export const NavMenu = (): JSX.Element => { Associations + + + Expenses + + Summary diff --git a/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx b/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx new file mode 100644 index 0000000..a3990fd --- /dev/null +++ b/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx @@ -0,0 +1,160 @@ +import React, { useMemo, useState } from 'react'; +import { Expense, UngroupedExpense } from '../../models'; +import NumberDisplay from '../NumberDisplay'; +import { TableFilter } from '../Filter'; + +interface Props { + expenses: UngroupedExpense[]; + loading: boolean; +} + +export default function ExpenseTable(props: Props): JSX.Element { + // ability to filter the unselected projects by project or PI + const [filter, setFilter] = useState(); + + const data = useMemo(() => { + if (filter) { + const lowerFilter = filter.toLowerCase(); + + return props.expenses.filter((exp) => { + // TODO: what do we want to filter on? + // For now, filter on everything + for (const value of Object.values(exp)) { + if (value && String(value).toLowerCase().includes(lowerFilter)) { + return true; + } + } + + // nothing matched, don't include this expense + return false; + }); + } else { + return props.expenses; + } + }, [filter, props.expenses]); + + if (props.loading) { + return ( +
+ Loading... +
+ ); + } + + return ( + <> +
+ +
+ + + + + + + + + + + + + + + {data.map((expense, i) => ( + + + + + + + + + + + ))} + + + + + + + + + + + + + + + + + + +
SFNChartOrgRProjectPIAccessionSpentFTE
{expense.sfn}{expense.chart}{expense.orgR}{expense.project}{expense.pi}{expense.accession} + + + +
Total + sum + exp.spent, 0)} + precision={2} + type='currency' + > + + sum + exp.fte, 0)} + precision={4} + > +
Associated + exp.isAssociated) + .reduce((sum, exp) => sum + exp.spent, 0)} + precision={2} + type='currency' + > + + exp.isAssociated) + .reduce((sum, exp) => sum + exp.fte, 0)} + precision={4} + > +
Unassociated + !exp.isAssociated) + .reduce((sum, exp) => sum + exp.spent, 0)} + precision={2} + type='currency' + > + + !exp.isAssociated) + .reduce((sum, exp) => sum + exp.fte, 0)} + precision={4} + > +
+ {data.length === 0 && ( +
+

+ No expenses found for given parameters. +

+
+ )} + + ); +} diff --git a/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx b/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx new file mode 100644 index 0000000..415e8aa --- /dev/null +++ b/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx @@ -0,0 +1,171 @@ +import React, { useEffect, useState, useCallback } from 'react'; + +import { useHistory } from 'react-router-dom'; + +import ExpenseTable from './ExpenseTable'; + +import { + Organization, + AssociationTotal, + SFNRecord, + UngroupedExpense, +} from '../../models'; +import Totals from '../summary/Totals'; + +const JSONHeader = { + 'Content-type': 'application/json; charset=UTF-8', +}; + +export default function ExpensesContainer(): JSX.Element { + const [orgs, setOrgs] = useState([]); + const [sfns, setSFNs] = useState([]); + const [selectedOrg, setSelectedOrg] = useState(); + const [selectedSFN, setSelectedSFN] = useState(); + const [totals, setTotals] = useState([]); + + const [expenses, setExpenses] = useState([]); + + const [expensesLoading, setExpensesLoading] = useState(true); + + const history = useHistory(); + + // fire only once to grab initial orgs + useEffect(() => { + const getDepartments = async (): Promise => { + // TODO: handle just getting orgs for current user + // TODO: handle api errors and possibly login issue errors + const result = await fetch('/Organization'); + const data = await result.json(); + + // need to allow any because the return type is odd + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const orgs: Organization[] = data.map((d: any) => { + return { + code: d.orgR, + name: d.name, + }; + }); + + if (data && data.length > 0) { + setOrgs(orgs); + setSelectedOrg(orgs[0]); + } else { + // no department access + history.push('/access'); + } + }; + + getDepartments(); // go grab the depts + }, [history]); + + useEffect(() => { + const getSFNs = async (): Promise => { + const result = await fetch('/Expense/SFNs'); + const data = await result.json(); + const sfns = [{ sfn: 'All', description: '-- All SFNs --' }, ...data]; + if (data && data.length > 0) { + setSFNs(sfns); + setSelectedSFN(sfns[0]); + } + }; + getSFNs(); + }, []); + + const getExpensesCallback = useCallback(async (): Promise => { + setExpensesLoading(true); + const result = await fetch( + `/Expense/Ungrouped?org=${selectedOrg?.code}&sfn=${selectedSFN?.sfn}` + ); + const expenses = await result.json(); + + setExpenses(expenses); + setExpensesLoading(false); + }, [selectedOrg, selectedSFN]); + + const getTotalsCallback = useCallback(async (): Promise => { + const result = await fetch( + `/Summary/ExpensesByDepartment/${selectedOrg?.code}` + ); + const data = (await result.json()) as AssociationTotal[]; + + setTotals(data); + }, [selectedOrg]); + + // requery whenever our grouping or org changes + useEffect(() => { + if (selectedOrg) { + getExpensesCallback(); + getTotalsCallback(); + } + }, [selectedOrg, getExpensesCallback, getTotalsCallback]); + + const orgSelected = (e: React.ChangeEvent): void => { + const val = e.target.value; + + const org = orgs.find((o) => o.code === val); + setSelectedOrg(org); + }; + + const sfnSelected = (e: React.ChangeEvent): void => { + const val = e.target.value; + + const sfn = sfns.find((s) => s.sfn === val); + setSelectedSFN(sfn); + }; + + return ( + <> +
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ +
+
+
+ + +
+
+
+ {/*
*/} +
+ + ); +} diff --git a/src/AD419/ClientApp/src/models/index.ts b/src/AD419/ClientApp/src/models/index.ts index 394adb7..425b50c 100644 --- a/src/AD419/ClientApp/src/models/index.ts +++ b/src/AD419/ClientApp/src/models/index.ts @@ -2,6 +2,10 @@ export interface Organization { code: string; name: string; } +export interface SFNRecord { + sfn: string; + description: string; +} export interface Project { project: string; @@ -27,6 +31,18 @@ export interface Expense { isAssociated: boolean; } +export interface UngroupedExpense { + sfn: string; + fte: number; + chart: string; + isAssociated: boolean; + orgR: string; + project: string; + accession: string; + pi: string; + spent: number; +} + export interface ExpenseGrouping { grouping: string; showAssociated: boolean; @@ -63,4 +79,4 @@ export interface SFNSummary { lineDisplayDescriptor: string; sfn: string; total: number; -} \ No newline at end of file +} diff --git a/src/AD419/Controllers/ExpenseController.cs b/src/AD419/Controllers/ExpenseController.cs index 85c1acc..84c2f45 100644 --- a/src/AD419/Controllers/ExpenseController.cs +++ b/src/AD419/Controllers/ExpenseController.cs @@ -35,6 +35,55 @@ public async Task Get(string org, string grouping, bool showAssoc commandType: CommandType.StoredProcedure)); } } + + [HttpGet("Ungrouped")] + public async Task Ungrouped(string org, string sfn) + { + if (!await _permissionService.CanAccessDepartment(User.Identity.Name, org)) + { + return Forbid(); + } + + using (var conn = _dbService.GetConnection()) + { + return Ok(await conn.QueryAsync(@" + SELECT + Expenses.Exp_SFN SFN, + Expenses.FTE, + Expenses.Chart, + Expenses.isAssociated, + Associations.OrgR, + Project.Project, + Project.Accession, + Project.inv1 PI, + Associations.Expenses Spent + FROM + Expenses INNER JOIN + ReportingOrg ON Expenses.OrgR = ReportingOrg.OrgR INNER JOIN + Associations ON Expenses.ExpenseID = Associations.ExpenseID INNER JOIN + Project ON Associations.Accession = Project.Accession INNER JOIN + SFN ON Expenses.Exp_SFN = SFN.SFN + WHERE + Associations.OrgR LIKE @OrgR AND + (@SFN LIKE 'ALL' OR Expenses.Exp_SFN LIKE @SFN) + ORDER BY SFN, Project, Associations.OrgR, [inv1] + ", + new { OrgR = org, SFN = sfn })); + } + } + + [HttpGet("SFNs")] + public async Task SFNs() + { + using (var conn = _dbService.GetConnection()) + { + // not using usp_getSFN because it filters out 204 + return Ok(await conn.QueryAsync(@" + SELECT SFN, SFN + ' (' + Description + ')' AS Description + FROM SFN + ORDER BY SFN")); + } + } } public class ExpenseModel @@ -47,4 +96,23 @@ public class ExpenseModel public int Num { get; set; } public bool IsAssociated { get; set; } } + + public class UngroupedExpenseModel + { + public string SFN { get; set; } + public decimal FTE { get; set; } + public string Chart { get; set; } + public bool IsAssociated { get; set; } + public string OrgR { get; set; } + public string Project { get; set; } + public string Accession { get; set; } + public string PI { get; set; } + public decimal Spent { get; set; } + } + + public class SFNModel + { + public string SFN { get; set; } + public string Description { get; set; } + } } From 6472b480be74a9727b74a73e53280c8a480459c0 Mon Sep 17 00:00:00 2001 From: Spruce Weber-Milne Date: Tue, 13 Dec 2022 10:30:39 -0800 Subject: [PATCH 2/5] Replace node-sass with sass --- src/AD419/ClientApp/package-lock.json | 664 +------------------------- src/AD419/ClientApp/package.json | 4 +- 2 files changed, 21 insertions(+), 647 deletions(-) diff --git a/src/AD419/ClientApp/package-lock.json b/src/AD419/ClientApp/package-lock.json index 100acdb..b34f150 100644 --- a/src/AD419/ClientApp/package-lock.json +++ b/src/AD419/ClientApp/package-lock.json @@ -1993,11 +1993,6 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==" }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -2120,11 +2115,6 @@ "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, "ansi-colors": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", @@ -2170,44 +2160,6 @@ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -2250,11 +2202,6 @@ "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" - }, "array-flatten": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", @@ -2373,11 +2320,6 @@ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" - }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", @@ -2935,14 +2877,6 @@ "file-uri-to-path": "1.0.0" } }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "requires": { - "inherits": "~2.0.0" - } - }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3312,22 +3246,6 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" - } - } - }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -3765,11 +3683,6 @@ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -4285,14 +4198,6 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==" }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "requires": { - "array-find-index": "^1.0.1" - } - }, "cyclist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", @@ -4495,11 +4400,6 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -6105,27 +6005,6 @@ "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", "optional": true }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - } - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -6136,62 +6015,6 @@ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "requires": { - "globule": "^1.0.0" - } - }, "gensync": { "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", @@ -6207,11 +6030,6 @@ "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" - }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -6315,16 +6133,6 @@ } } }, - "globule": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", - "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.12", - "minimatch": "~3.0.2" - } - }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", @@ -6413,11 +6221,6 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -6766,6 +6569,11 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz", "integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==" }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==" + }, "import-cwd": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", @@ -6805,11 +6613,6 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, - "in-publish": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", - "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==" - }, "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", @@ -7023,11 +6826,6 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" - }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -7152,11 +6950,6 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" - }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -7792,11 +7585,6 @@ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" }, - "js-base64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", - "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8168,15 +7956,6 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, "lower-case": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", @@ -8247,11 +8026,6 @@ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" - }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -8328,104 +8102,6 @@ } } }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "requires": { - "is-utf8": "^0.2.0" - } - } - } - }, "merge": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", @@ -8729,7 +8405,8 @@ "nan": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "optional": true }, "nanomatch": { "version": "1.2.13", @@ -8795,40 +8472,6 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==" }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" - } - } - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -8944,98 +8587,6 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.56.tgz", "integrity": "sha512-EVo605FhWLygH8a64TjgpjyHYOihkxECwX1bHHr8tETJKWEiWS2YJjPbvsX2jFjnjTNEgBCmk9mLjKG1Mf11cw==" }, - "node-sass": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", - "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==", - "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash": "^4.17.15", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.13.2", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "2.2.5", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - } - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "requires": { - "abbrev": "1" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -9086,17 +8637,6 @@ "path-key": "^2.0.0" } }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -9359,11 +8899,6 @@ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, "os-locale": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", @@ -9379,15 +8914,6 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -10798,11 +10324,6 @@ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -11629,25 +11150,6 @@ "minimatch": "3.0.4" } }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - }, - "dependencies": { - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "requires": { - "repeating": "^2.0.0" - } - } - } - }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", @@ -11808,14 +11310,6 @@ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "requires": { - "is-finite": "^1.0.0" - } - }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -12107,15 +11601,14 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-10.0.0.tgz", "integrity": "sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg==" }, - "sass-graph": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", - "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", + "sass": { + "version": "1.56.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.56.2.tgz", + "integrity": "sha512-ciEJhnyCRwzlBCB+h5cCPM6ie/6f8HrhZMQOf5vlU60Y1bI1rx5Zb0vlDZvaycHsg/MqFfF1Eq2eokAa32iw8w==", "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^13.3.2" + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" } }, "sass-loader": { @@ -12207,25 +11700,6 @@ "compute-scroll-into-view": "^1.0.14" } }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -12670,6 +12144,11 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -12828,43 +12307,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", - "requires": { - "readable-stream": "^2.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "stealthy-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", @@ -13115,14 +12557,6 @@ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "requires": { - "get-stdin": "^4.0.1" - } - }, "strip-json-comments": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", @@ -13247,16 +12681,6 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" }, - "tar": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - } - }, "terser": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz", @@ -13553,19 +12977,6 @@ "punycode": "^2.1.0" } }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" - }, - "true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "requires": { - "glob": "^7.1.2" - } - }, "ts-pnp": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz", @@ -14684,43 +14095,6 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/src/AD419/ClientApp/package.json b/src/AD419/ClientApp/package.json index 3451867..57aa426 100644 --- a/src/AD419/ClientApp/package.json +++ b/src/AD419/ClientApp/package.json @@ -11,7 +11,6 @@ "bootstrap": "^4.5.0", "jquery": "^3.5.1", "merge": "^1.2.1", - "node-sass": "^4.14.1", "react": "^16.13.1", "react-bootstrap-typeahead": "^5.1.1", "react-dom": "^16.13.1", @@ -19,7 +18,8 @@ "react-router-dom": "^5.2.0", "react-scripts": "^3.4.1", "reactstrap": "^8.4.1", - "rimraf": "^3.0.2" + "rimraf": "^3.0.2", + "sass": "^1.56.2" }, "devDependencies": { "@types/react-bootstrap-typeahead": "^3.4.6", From 1bc4b84b793fda3cfd26e427b35ca891a8914811 Mon Sep 17 00:00:00 2001 From: Spruce Weber-Milne Date: Thu, 5 Jan 2023 11:21:37 -0800 Subject: [PATCH 3/5] Use existing sprocs --- .../src/components/expenses/ExpenseTable.tsx | 70 ++----------------- src/AD419/ClientApp/src/models/index.ts | 9 +-- src/AD419/Controllers/ExpenseController.cs | 40 ++--------- 3 files changed, 13 insertions(+), 106 deletions(-) diff --git a/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx b/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx index a3990fd..a829349 100644 --- a/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx +++ b/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx @@ -50,101 +50,39 @@ export default function ExpenseTable(props: Props): JSX.Element { SFN - Chart OrgR Project PI - Accession Spent - FTE {data.map((expense, i) => ( - + {expense.sfn} - {expense.chart} {expense.orgR} {expense.project} {expense.pi} - {expense.accession} - - - ))} - Total - - sum + exp.spent, 0)} - precision={2} - type='currency' - > - - - sum + exp.fte, 0)} - precision={4} - > - - - - Associated + Total exp.isAssociated) - .reduce((sum, exp) => sum + exp.spent, 0)} + value={data.reduce((sum, exp) => sum + exp.expenses, 0)} precision={2} type='currency' > - - exp.isAssociated) - .reduce((sum, exp) => sum + exp.fte, 0)} - precision={4} - > - - - - Unassociated - - !exp.isAssociated) - .reduce((sum, exp) => sum + exp.spent, 0)} - precision={2} - type='currency' - > - - - !exp.isAssociated) - .reduce((sum, exp) => sum + exp.fte, 0)} - precision={4} - > - diff --git a/src/AD419/ClientApp/src/models/index.ts b/src/AD419/ClientApp/src/models/index.ts index 425b50c..4e87883 100644 --- a/src/AD419/ClientApp/src/models/index.ts +++ b/src/AD419/ClientApp/src/models/index.ts @@ -32,15 +32,12 @@ export interface Expense { } export interface UngroupedExpense { + expenseId: number; sfn: string; - fte: number; - chart: string; - isAssociated: boolean; - orgR: string; project: string; - accession: string; + expenses: number; + orgR: string; pi: string; - spent: number; } export interface ExpenseGrouping { diff --git a/src/AD419/Controllers/ExpenseController.cs b/src/AD419/Controllers/ExpenseController.cs index 84c2f45..a6cb028 100644 --- a/src/AD419/Controllers/ExpenseController.cs +++ b/src/AD419/Controllers/ExpenseController.cs @@ -46,29 +46,8 @@ public async Task Ungrouped(string org, string sfn) using (var conn = _dbService.GetConnection()) { - return Ok(await conn.QueryAsync(@" - SELECT - Expenses.Exp_SFN SFN, - Expenses.FTE, - Expenses.Chart, - Expenses.isAssociated, - Associations.OrgR, - Project.Project, - Project.Accession, - Project.inv1 PI, - Associations.Expenses Spent - FROM - Expenses INNER JOIN - ReportingOrg ON Expenses.OrgR = ReportingOrg.OrgR INNER JOIN - Associations ON Expenses.ExpenseID = Associations.ExpenseID INNER JOIN - Project ON Associations.Accession = Project.Accession INNER JOIN - SFN ON Expenses.Exp_SFN = SFN.SFN - WHERE - Associations.OrgR LIKE @OrgR AND - (@SFN LIKE 'ALL' OR Expenses.Exp_SFN LIKE @SFN) - ORDER BY SFN, Project, Associations.OrgR, [inv1] - ", - new { OrgR = org, SFN = sfn })); + return Ok(await conn.QueryAsync("usp_getProjectExpenses", + new { OrgR = org, SFN = sfn }, commandType: CommandType.StoredProcedure)); } } @@ -77,11 +56,7 @@ public async Task SFNs() { using (var conn = _dbService.GetConnection()) { - // not using usp_getSFN because it filters out 204 - return Ok(await conn.QueryAsync(@" - SELECT SFN, SFN + ' (' + Description + ')' AS Description - FROM SFN - ORDER BY SFN")); + return Ok(await conn.QueryAsync("usp_getSFN", commandType: CommandType.StoredProcedure)); } } } @@ -99,15 +74,12 @@ public class ExpenseModel public class UngroupedExpenseModel { + public int ExpenseId { get; set; } public string SFN { get; set; } - public decimal FTE { get; set; } - public string Chart { get; set; } - public bool IsAssociated { get; set; } - public string OrgR { get; set; } public string Project { get; set; } - public string Accession { get; set; } + public decimal Expenses { get; set; } + public string OrgR { get; set; } public string PI { get; set; } - public decimal Spent { get; set; } } public class SFNModel From 944483833abfbc72694fbcdf448638dfac71e737 Mon Sep 17 00:00:00 2001 From: Spruce Weber-Milne Date: Thu, 5 Jan 2023 14:06:36 -0800 Subject: [PATCH 4/5] Replace department totals with SFN subtotal footer --- .../src/components/expenses/ExpenseTable.tsx | 25 +++++++++------ .../components/expenses/ExpensesContainer.tsx | 32 ++----------------- src/AD419/ClientApp/src/utilities.ts | 8 +++++ 3 files changed, 26 insertions(+), 39 deletions(-) create mode 100644 src/AD419/ClientApp/src/utilities.ts diff --git a/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx b/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx index a829349..6a98ce1 100644 --- a/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx +++ b/src/AD419/ClientApp/src/components/expenses/ExpenseTable.tsx @@ -1,7 +1,8 @@ import React, { useMemo, useState } from 'react'; -import { Expense, UngroupedExpense } from '../../models'; +import { UngroupedExpense } from '../../models'; import NumberDisplay from '../NumberDisplay'; import { TableFilter } from '../Filter'; +import { groupBy } from '../../utilities'; interface Props { expenses: UngroupedExpense[]; @@ -75,15 +76,21 @@ export default function ExpenseTable(props: Props): JSX.Element { - Total - - sum + exp.expenses, 0)} - precision={2} - type='currency' - > - + + SFN Subtotal + {groupBy(data, (d) => d.sfn).map(([sfn, expenses]) => ( + + {sfn} + + sum + exp.expenses, 0)} + precision={2} + type='currency' + > + + + ))} {data.length === 0 && ( diff --git a/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx b/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx index 415e8aa..f79a405 100644 --- a/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx +++ b/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx @@ -4,24 +4,13 @@ import { useHistory } from 'react-router-dom'; import ExpenseTable from './ExpenseTable'; -import { - Organization, - AssociationTotal, - SFNRecord, - UngroupedExpense, -} from '../../models'; -import Totals from '../summary/Totals'; - -const JSONHeader = { - 'Content-type': 'application/json; charset=UTF-8', -}; +import { Organization, SFNRecord, UngroupedExpense } from '../../models'; export default function ExpensesContainer(): JSX.Element { const [orgs, setOrgs] = useState([]); const [sfns, setSFNs] = useState([]); const [selectedOrg, setSelectedOrg] = useState(); const [selectedSFN, setSelectedSFN] = useState(); - const [totals, setTotals] = useState([]); const [expenses, setExpenses] = useState([]); @@ -82,22 +71,12 @@ export default function ExpensesContainer(): JSX.Element { setExpensesLoading(false); }, [selectedOrg, selectedSFN]); - const getTotalsCallback = useCallback(async (): Promise => { - const result = await fetch( - `/Summary/ExpensesByDepartment/${selectedOrg?.code}` - ); - const data = (await result.json()) as AssociationTotal[]; - - setTotals(data); - }, [selectedOrg]); - // requery whenever our grouping or org changes useEffect(() => { if (selectedOrg) { getExpensesCallback(); - getTotalsCallback(); } - }, [selectedOrg, getExpensesCallback, getTotalsCallback]); + }, [selectedOrg, getExpensesCallback]); const orgSelected = (e: React.ChangeEvent): void => { const val = e.target.value; @@ -157,14 +136,7 @@ export default function ExpensesContainer(): JSX.Element { expenses={expenses} > -
-
- - -
-
- {/*
*/} ); diff --git a/src/AD419/ClientApp/src/utilities.ts b/src/AD419/ClientApp/src/utilities.ts new file mode 100644 index 0000000..c1ea3b9 --- /dev/null +++ b/src/AD419/ClientApp/src/utilities.ts @@ -0,0 +1,8 @@ +export const groupBy = (arr: T[], key: (i: T) => K) => + Object.entries( + arr.reduce((groups, item) => { + groups[key(item)] = groups[key(item)] || []; + groups[key(item)].push(item); + return groups; + }, {} as Record) + ); From 8273b1e6f6ce2faf7717a7091f0b63783be7c53a Mon Sep 17 00:00:00 2001 From: Spruce Weber-Milne Date: Thu, 5 Jan 2023 14:47:38 -0800 Subject: [PATCH 5/5] remove comments --- .../ClientApp/src/components/expenses/ExpensesContainer.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx b/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx index f79a405..9d8c167 100644 --- a/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx +++ b/src/AD419/ClientApp/src/components/expenses/ExpensesContainer.tsx @@ -21,8 +21,6 @@ export default function ExpensesContainer(): JSX.Element { // fire only once to grab initial orgs useEffect(() => { const getDepartments = async (): Promise => { - // TODO: handle just getting orgs for current user - // TODO: handle api errors and possibly login issue errors const result = await fetch('/Organization'); const data = await result.json();