From 6814e180a677d1ea9c47159fb7baae4e4e53429c Mon Sep 17 00:00:00 2001 From: fdelemarre Date: Thu, 27 Jun 2024 20:01:51 +0200 Subject: [PATCH] Add Median and % Target Met row --- .../PerformanceOverviewTable.tsx | 67 ++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/webapp/components/table/performance-overview-table/PerformanceOverviewTable.tsx b/src/webapp/components/table/performance-overview-table/PerformanceOverviewTable.tsx index 5bb11cec..daea1a4d 100644 --- a/src/webapp/components/table/performance-overview-table/PerformanceOverviewTable.tsx +++ b/src/webapp/components/table/performance-overview-table/PerformanceOverviewTable.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import _ from "lodash"; import { Table, @@ -66,6 +66,9 @@ export const PerformanceOverviewTable: React.FC = notify1d: 1, respond7d: 7, }; + + const calculateColumns = [...editRiskAssessmentColumns, ..._.keys(columnRules)]; + useEffect(() => { if (searchTerm === "") { setFilteredRows(rows); @@ -104,6 +107,58 @@ export const PerformanceOverviewTable: React.FC = return value <= rule ? "green" : "red"; }; + const calculateMedian = ( + rows: PerformanceOverviewTableProps["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; + }; + + const calculatePercentTargetMet = ( + rows: PerformanceOverviewTableProps["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}%`; + }; + + const buildMedianRow = useMemo(() => { + return columns.map((column, columnIndex) => ( + + {columnIndex === 0 && "Median"} + {_.includes(calculateColumns, column.value) + ? calculateMedian(filteredRows, column.value) + : ""} + + )); + }, [filteredRows]); + + const buildPercentTargetMetRow = useMemo(() => { + return columns.map((column, columnIndex) => { + const rule = columnRules[column.value] || 7; + + return ( + + {columnIndex === 0 && "% Target Met"} + + {_.includes(calculateColumns, column.value) + ? calculatePercentTargetMet(filteredRows, column.value, rule) + : ""} + + ); + }); + }, [filteredRows]); + return ( @@ -146,6 +201,8 @@ export const PerformanceOverviewTable: React.FC = ))} ))} + {buildMedianRow} + {buildPercentTargetMetRow} @@ -164,7 +221,7 @@ const StyledTableContainer = styled(TableContainer)` } & .MuiTableHead-root { color: ${props => props.theme.palette.common.greyBlack}; - background-color: ${props => props.theme.palette.common.greyLight}; + background-color: ${props => props.theme.palette.common.grey4}; } & .MuiTableBody-root { color: ${props => props.theme.palette.common.grey}; @@ -193,6 +250,12 @@ const BodyTableCell = styled(TableCell)<{ color?: string; $boldUnderline: boolea font-weight: ${props => (props.$boldUnderline || !!props.color) && "600"}; `; +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"}; +`; + const Container = styled.div` display: flex; justify-content: space-between;