From 16b3247c890e8d31af6d4d15870c393a8b1f5cbb Mon Sep 17 00:00:00 2001 From: fdelemarre Date: Wed, 10 Jul 2024 10:56:03 +0200 Subject: [PATCH] move calculation logic in `useStatisticCalculations` hook and refactor into a generic `CalculationRow` component --- i18n/en.pot | 10 ++- i18n/es.po | 8 ++- .../table/statistic-table/CalculationRow.tsx | 35 +++++++++++ .../table/statistic-table/MedianRow.tsx | 57 ----------------- .../statistic-table/PercentTargetMetRow.tsx | 63 ------------------- .../table/statistic-table/StatisticTable.tsx | 21 ++++--- src/webapp/hooks/useStatisticCalculations.ts | 36 +++++++++++ 7 files changed, 99 insertions(+), 131 deletions(-) create mode 100644 src/webapp/components/table/statistic-table/CalculationRow.tsx delete mode 100644 src/webapp/components/table/statistic-table/MedianRow.tsx delete mode 100644 src/webapp/components/table/statistic-table/PercentTargetMetRow.tsx create mode 100644 src/webapp/hooks/useStatisticCalculations.ts diff --git a/i18n/en.pot b/i18n/en.pot index f650ff8c..57c3229b 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-07-08T08:33:37.522Z\n" -"PO-Revision-Date: 2024-07-08T08:33:37.523Z\n" +"POT-Creation-Date: 2024-07-10T08:54:31.455Z\n" +"PO-Revision-Date: 2024-07-10T08:54:31.455Z\n" msgid "Add new option" msgstr "" @@ -32,6 +32,12 @@ msgstr "" msgid "Last updated: " msgstr "" +msgid "Median" +msgstr "" + +msgid "% Target Met" +msgstr "" + msgid "Dashboard" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 1c0ccad0..65725875 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2024-07-08T08:33:37.522Z\n" +"POT-Creation-Date: 2024-07-10T08:54:31.455Z\n" "PO-Revision-Date: 2018-10-25T09:02:35.143Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -32,6 +32,12 @@ msgstr "" msgid "Last updated: " msgstr "" +msgid "Median" +msgstr "" + +msgid "% Target Met" +msgstr "" + msgid "Dashboard" msgstr "" diff --git a/src/webapp/components/table/statistic-table/CalculationRow.tsx b/src/webapp/components/table/statistic-table/CalculationRow.tsx new file mode 100644 index 00000000..454dec22 --- /dev/null +++ b/src/webapp/components/table/statistic-table/CalculationRow.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { TableCell, TableRow } from "@material-ui/core"; +import styled from "styled-components"; +import { TableColumn } from "./StatisticTable"; + +type CalculationRowProps = { + columns: TableColumn[]; + calculateColumns: string[]; + label: string; + calculate: (column: string) => string | number; +}; + +export const CalculationRow: React.FC = React.memo( + ({ columns, calculateColumns, label, calculate }) => { + const [labelColumn, ...otherColumns] = columns; + return ( + + + {label} + + {otherColumns.map(column => ( + + {calculateColumns.includes(column.value) ? calculate(column.value) : ""} + + ))} + + ); + } +); + +const FooterTableCell = styled(TableCell)<{ $boldUnderline?: boolean }>` + background-color: ${props => props.theme.palette.common.greyLight}; + text-decoration: ${props => props.$boldUnderline && "underline"}; + font-weight: ${props => (props.$boldUnderline || !!props.color) && "600"}; +`; diff --git a/src/webapp/components/table/statistic-table/MedianRow.tsx b/src/webapp/components/table/statistic-table/MedianRow.tsx deleted file mode 100644 index 98d45439..00000000 --- a/src/webapp/components/table/statistic-table/MedianRow.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from "react"; -import _ from "../../../../domain/entities/generic/Collection"; -import { TableCell, TableRow } from "@material-ui/core"; -import styled from "styled-components"; -import { StatisticTableProps } from "./StatisticTable"; - -export type TableColumn = { - value: string; - label: string; - dark?: boolean; -}; - -type MedianRowProps = { - columns: TableColumn[]; - rows: { - [key: TableColumn["value"]]: string; - }[]; - calculateColumns: TableColumn["value"][]; -}; - -export const MedianRow: React.FC = React.memo( - ({ rows, columns, calculateColumns }) => { - const calculateMedian = ( - rows: StatisticTableProps["rows"], - column: TableColumn["value"] - ) => { - const values = rows.map(row => Number(row[column])).filter(value => !isNaN(value)); - values.sort((a, b) => a - b); - const mid = Math.floor(values.length / 2); - return values.length % 2 !== 0 - ? values[mid] - : ((values[mid - 1] || 0) + (values[mid] || 0)) / 2; - }; - - return ( - - {columns.map((column, columnIndex) => ( - - {columnIndex === 0 && "Median"} - {calculateColumns.includes(column.value) - ? calculateMedian(rows, column.value) - : ""} - - ))} - - ); - } -); - -const FooterTableCell = styled(TableCell)<{ $boldUnderline: boolean }>` - background-color: ${props => props.theme.palette.common.greyLight}; - text-decoration: ${props => props.$boldUnderline && "underline"}; - font-weight: ${props => (props.$boldUnderline || !!props.color) && "600"}; -`; diff --git a/src/webapp/components/table/statistic-table/PercentTargetMetRow.tsx b/src/webapp/components/table/statistic-table/PercentTargetMetRow.tsx deleted file mode 100644 index a93b7ed3..00000000 --- a/src/webapp/components/table/statistic-table/PercentTargetMetRow.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from "react"; -import _ from "../../../../domain/entities/generic/Collection"; -import { TableCell, TableRow } from "@material-ui/core"; -import styled from "styled-components"; -import { StatisticTableProps } from "./StatisticTable"; - -export type TableColumn = { - value: string; - label: string; - dark?: boolean; -}; - -type PercentTargetMetRowProps = { - columns: TableColumn[]; - rows: { - [key: TableColumn["value"]]: string; - }[]; - columnRules: { - [key: TableColumn["value"]]: number; - }; - calculateColumns: TableColumn["value"][]; -}; - -export const PercentTargetMetRow: React.FC = React.memo( - ({ rows, columns, columnRules, calculateColumns }) => { - const calculatePercentTargetMet = ( - rows: StatisticTableProps["rows"], - column: TableColumn["value"], - target: number - ) => { - const count = rows.filter(row => Number(row[column]) <= target).length; - const percentage = (count / rows.length) * 100 || 0; - return `${percentage.toFixed(0) || 0}%`; - }; - - return ( - - {columns.map((column, columnIndex) => { - const rule = columnRules[column.value] || 7; - - return ( - - {columnIndex === 0 && "% Target Met"} - - {calculateColumns.includes(column.value) - ? calculatePercentTargetMet(rows, column.value, rule) - : ""} - - ); - })} - - ); - } -); - -const FooterTableCell = styled(TableCell)<{ $boldUnderline: boolean }>` - background-color: ${props => props.theme.palette.common.greyLight}; - text-decoration: ${props => props.$boldUnderline && "underline"}; - font-weight: ${props => (props.$boldUnderline || !!props.color) && "600"}; -`; diff --git a/src/webapp/components/table/statistic-table/StatisticTable.tsx b/src/webapp/components/table/statistic-table/StatisticTable.tsx index 8a9d76e3..37f134ff 100644 --- a/src/webapp/components/table/statistic-table/StatisticTable.tsx +++ b/src/webapp/components/table/statistic-table/StatisticTable.tsx @@ -1,5 +1,6 @@ import React from "react"; import styled from "styled-components"; +import i18n from "../../../../utils/i18n"; import { Table, TableBody, @@ -9,13 +10,12 @@ import { TableContainer, } from "@material-ui/core"; import { SearchInput } from "../../search-input/SearchInput"; -import i18n from "../../../../utils/i18n"; -import { MedianRow } from "./MedianRow"; -import { PercentTargetMetRow } from "./PercentTargetMetRow"; import { MultipleSelector } from "../../selector/MultipleSelector"; import { useTableFilters } from "../../../hooks/useTableFilters"; import { useTableCell } from "../../../hooks/useTableCell"; +import { useStatisticCalculations } from "../../../hooks/useStatisticCalculations"; import { ColoredCell } from "./ColoredCell"; +import { CalculationRow } from "./CalculationRow"; export type TableColumn = { value: string; @@ -52,6 +52,10 @@ export const StatisticTable: React.FC = React.memo( const { searchTerm, setSearchTerm, filters, setFilters, filteredRows, filterOptions } = useTableFilters(rows, filtersConfig); const { getCellColor } = useTableCell(editRiskAssessmentColumns, columnRules); + const { calculateMedian, calculatePercentTargetMet } = useStatisticCalculations( + filteredRows, + columnRules + ); return ( @@ -105,16 +109,17 @@ export const StatisticTable: React.FC = React.memo( )} ))} - - diff --git a/src/webapp/hooks/useStatisticCalculations.ts b/src/webapp/hooks/useStatisticCalculations.ts new file mode 100644 index 00000000..40ddeda5 --- /dev/null +++ b/src/webapp/hooks/useStatisticCalculations.ts @@ -0,0 +1,36 @@ +import { useCallback } from "react"; +import { StatisticTableProps } from "../components/table/statistic-table/StatisticTable"; + +export const useStatisticCalculations = ( + rows: StatisticTableProps["rows"], + columnRules: { [key: string]: number } +) => { + const calculateMedian = useCallback( + (column: string) => { + const values = rows.map(row => Number(row[column])).filter(value => !isNaN(value)); + values.sort((a, b) => a - b); + const mid = Math.floor(values.length / 2); + return ( + (values.length % 2 !== 0 + ? values[mid] + : ((values[mid - 1] || 0) + (values[mid] || 0)) / 2) || 0 + ); + }, + [rows] + ); + + const calculatePercentTargetMet = useCallback( + (column: string) => { + const target = columnRules[column] || 7; + const count = rows.filter(row => Number(row[column]) <= target).length; + const percentage = (count / rows.length) * 100 || 0; + return `${percentage.toFixed(0) || 0}%`; + }, + [rows, columnRules] + ); + + return { + calculateMedian, + calculatePercentTargetMet, + }; +};