From f889da0c7b2028bac98b8d9cd4659abae8e67aa4 Mon Sep 17 00:00:00 2001 From: fdelemarre Date: Wed, 3 Jul 2024 12:21:19 +0200 Subject: [PATCH] filters config as component props handle MultiSelector filters --- i18n/en.pot | 7 +- i18n/es.po | 5 +- .../StatisticTable.tsx | 113 +++++++++++------- src/webapp/pages/dashboard/DashboardPage.tsx | 9 ++ 4 files changed, 85 insertions(+), 49 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 523a4e02..9e906c0e 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-06-28T07:18:27.936Z\n" -"PO-Revision-Date: 2024-06-28T07:18:27.936Z\n" +"POT-Creation-Date: 2024-07-03T10:20:12.524Z\n" +"PO-Revision-Date: 2024-07-03T10:20:12.524Z\n" msgid "Add new option" msgstr "" @@ -35,9 +35,6 @@ msgstr "" msgid "Last updated: " msgstr "" -msgid "Filter" -msgstr "" - msgid "Incident Management Team Builder" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index c2426959..cb4c29d7 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-06-28T07:18:27.936Z\n" +"POT-Creation-Date: 2024-07-03T10:20:12.524Z\n" "PO-Revision-Date: 2018-10-25T09:02:35.143Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -35,9 +35,6 @@ msgstr "" msgid "Last updated: " msgstr "" -msgid "Filter" -msgstr "" - msgid "Incident Management Team Builder" msgstr "" diff --git a/src/webapp/components/table/performance-overview-table/StatisticTable.tsx b/src/webapp/components/table/performance-overview-table/StatisticTable.tsx index a4146cc2..3726d8e1 100644 --- a/src/webapp/components/table/performance-overview-table/StatisticTable.tsx +++ b/src/webapp/components/table/performance-overview-table/StatisticTable.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from "react"; +import React, { useCallback, useMemo, useState } from "react"; import _ from "../../../../domain/entities/generic/Collection"; import { Table, @@ -10,11 +10,11 @@ import { } from "@material-ui/core"; import styled from "styled-components"; import { SearchInput } from "../../search-input/SearchInput"; -import { Selector } from "../../selector/Selector"; import { Maybe } from "../../../../utils/ts-utils"; import i18n from "../../../../utils/i18n"; import { MedianRow } from "./MedianRow"; import { PercentTargetMetRow } from "./PercentTargetMetRow"; +import { MultipleSelector } from "../../multiple-selector/MultipleSelector"; export type TableColumn = { value: string; @@ -22,6 +22,16 @@ export type TableColumn = { dark?: boolean; }; +export type FilterType = { + value: TableColumn["value"]; + label: TableColumn["label"]; + type: "multiselector" | "datepicker"; +}; + +export type FiltersValuesType = { + [key: TableColumn["value"]]: string[]; +}; + export type StatisticTableProps = { columns: TableColumn[]; columnRules: { @@ -31,17 +41,36 @@ export type StatisticTableProps = { rows: { [key: TableColumn["value"]]: string; }[]; + filters: FilterType[]; }; export const StatisticTable: React.FC = React.memo( - ({ rows, columns, columnRules, editRiskAssessmentColumns }) => { + ({ rows, columns, columnRules, editRiskAssessmentColumns, filters: filtersConfig }) => { const [searchTerm, setSearchTerm] = useState(""); - const [filterValue, setFilterValue] = useState(""); - const calculateColumns = [...editRiskAssessmentColumns, ...Object.keys(columnRules)]; + const [filters, setFilters] = useState( + filtersConfig.reduce((acc: FiltersValuesType, filter) => { + acc[filter.value] = []; + return acc; + }, {}) + ); - const filteredRows = useTableSearch(rows, searchTerm); + const calculateColumns = [...editRiskAssessmentColumns, ...Object.keys(columnRules)]; + const filteredRows = useTableSearch(rows, searchTerm, filters); + + const filterOptions = useCallback( + (column: TableColumn["value"]) => { + return _(rows) + .map(row => ({ + value: row[column] || "", + label: row[column] || "", + })) + .uniqBy(filter => filter.value) + .value(); + }, + [rows] + ); const getCellColor = (cellValue: Maybe, column: TableColumn["value"]) => { // Return "orange" for empty Edit Risk Assessment column if (!cellValue) { @@ -70,21 +99,22 @@ export const StatisticTable: React.FC = React.memo( return ( - - { - setFilterValue(value); - console.log(value); - }} - /> - - - setSearchTerm(value)} /> - + {_(filtersConfig) + .map(({ value, label }) => ( + { + console.log(values); + setFilters({ ...filters, [value]: values }); + }} + /> + )) + .value()} + setSearchTerm(value)} /> @@ -130,20 +160,37 @@ export const StatisticTable: React.FC = React.memo( } ); -const useTableSearch = (rows: StatisticTableProps["rows"], searchTerm: string) => { +const useTableSearch = ( + rows: StatisticTableProps["rows"], + searchTerm: string, + filters: FiltersValuesType +) => { return useMemo(() => { - if (searchTerm === "") { + const allFiltersEmpty = Object.keys(filters).every( + key => (filters[key] || []).length === 0 + ); + + if (searchTerm === "" && allFiltersEmpty) { return rows; } else { return _(rows) .filter(row => { - return _(Object.values(row)).some(cell => { + // Filter by filters values + const matchesFilters = Object.keys(filters).every(key => { + const filterValues = filters[key] || []; + if (filterValues.length === 0) return true; + return filterValues.includes(row[key] || ""); + }); + + const matchesSearchTerm = _(Object.values(row)).some(cell => { return cell.toLowerCase().includes(searchTerm.toLowerCase()); }); + + return matchesFilters && matchesSearchTerm; }) .value(); } - }, [rows, searchTerm]); + }, [rows, searchTerm, filters]); }; const StyledTableContainer = styled(TableContainer)` @@ -185,25 +232,11 @@ 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; align-items: center; margin-bottom: 1rem; width: 100%; + gap: 1rem; `; - -const Wrapper = styled.div<{ width: string }>` - width: ${({ width }) => width}; - max-width: ${({ width }) => width}; -`; - -const StyledSelectorWrapper = styled(Wrapper).attrs({ width: "10rem" })``; - -const StyledSearchInputWrapper = styled(Wrapper).attrs({ width: "19rem" })``; diff --git a/src/webapp/pages/dashboard/DashboardPage.tsx b/src/webapp/pages/dashboard/DashboardPage.tsx index e5046236..f52e4876 100644 --- a/src/webapp/pages/dashboard/DashboardPage.tsx +++ b/src/webapp/pages/dashboard/DashboardPage.tsx @@ -4,6 +4,7 @@ import i18n from "../../../utils/i18n"; import { Layout } from "../../components/layout/Layout"; import { Section } from "../../components/section/Section"; import { + FilterType, StatisticTable, TableColumn, } from "../../components/table/performance-overview-table/StatisticTable"; @@ -179,6 +180,13 @@ export const DashboardPage: React.FC = React.memo(() => { respond7d: "12", }, ]; + const filters: FilterType[] = [ + { value: "event", label: "Event", type: "multiselector" }, + // { value: "name", label: "Name", type: "multiselector" }, + // { value: "disease", label: "Disease", type: "multiselector" }, + { value: "location", label: "Location", type: "multiselector" }, + // { value: "dateRange", label: "Date range", type: "datepicker" }, + ]; return ( @@ -191,6 +199,7 @@ export const DashboardPage: React.FC = React.memo(() => {