From 85f184cc34f41cd022e770575df2f308671350c7 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Tue, 14 Jan 2025 16:50:34 +0100 Subject: [PATCH] Create two tabs to split dashboard in alerts and national source of data --- .../table/statistic-table/StatisticTable.tsx | 2 +- .../pages/dashboard/AlertsDashboard.tsx | 210 ++++++++++++++++++ src/webapp/pages/dashboard/DashboardPage.tsx | 192 ++++------------ .../pages/dashboard/NationalDashboard.tsx | 88 ++++++++ .../dashboard/useAlertsPerformanceOverview.ts | 4 +- .../useNationalPerformanceOverview.ts | 2 +- src/webapp/pages/dashboard/useTabs.ts | 22 ++ 7 files changed, 367 insertions(+), 153 deletions(-) create mode 100644 src/webapp/pages/dashboard/AlertsDashboard.tsx create mode 100644 src/webapp/pages/dashboard/NationalDashboard.tsx create mode 100644 src/webapp/pages/dashboard/useTabs.ts diff --git a/src/webapp/components/table/statistic-table/StatisticTable.tsx b/src/webapp/components/table/statistic-table/StatisticTable.tsx index 5b817f12..8372208a 100644 --- a/src/webapp/components/table/statistic-table/StatisticTable.tsx +++ b/src/webapp/components/table/statistic-table/StatisticTable.tsx @@ -260,7 +260,7 @@ const HeadTableCell = styled(TableCell)<{ $dark?: boolean }>` const StyledTableRow = styled(TableRow)<{ $isHighlighted?: boolean }>` background-color: ${props => - props.$isHighlighted ? props.theme.palette.common.greyLight2 : "initial"}; + props.$isHighlighted ? props.theme.palette.common.green100 : "initial"}; `; const StyledTableCell = styled(TableCell)<{ $link?: boolean }>` diff --git a/src/webapp/pages/dashboard/AlertsDashboard.tsx b/src/webapp/pages/dashboard/AlertsDashboard.tsx new file mode 100644 index 00000000..1415dc3e --- /dev/null +++ b/src/webapp/pages/dashboard/AlertsDashboard.tsx @@ -0,0 +1,210 @@ +import React, { Dispatch, SetStateAction } from "react"; +import styled from "styled-components"; + +import { DateRangePicker } from "../../components/date-picker/DateRangePicker"; +import LoaderContainer from "../../components/loader/LoaderContainer"; +import { MapSection } from "../../components/map/MapSection"; +import { Section } from "../../components/section/Section"; +import { MultipleSelector } from "../../components/selector/MultipleSelector"; +import { Selector } from "../../components/selector/Selector"; +import { StatsCard } from "../../components/stats-card/StatsCard"; +import { + FiltersConfig, + FiltersValuesType, + StatisticTable, + TableColumn, +} from "../../components/table/statistic-table/StatisticTable"; +import { PerformanceMetric717 } from "./use717Performance"; +import i18n from "../../../utils/i18n"; +import { Pagination } from "../../components/pagination/Pagination"; +import { SelectorFiltersConfig } from "./useAlertsActiveVerifiedFilters"; +import { TotalCardCounts } from "../../../domain/entities/disease-outbreak-event/PerformanceOverviewMetrics"; +import { AlertsPerformanceOverviewMetricsTableData, Order } from "./useAlertsPerformanceOverview"; +import { Maybe } from "../../../utils/ts-utils"; + +export type AlertsDashboardProps = { + selectorFiltersConfig: SelectorFiltersConfig[]; + singleSelectFilters: Record; + setSingleSelectFilters: (id: string, value: string) => void; + multiSelectFilters: Record; + setMultiSelectFilters: (id: string, values: string[]) => void; + dateRangeFilter: { + onChange: (value: string[]) => void; + value: string[]; + }; + + cardCountsLoading: boolean; + cardCounts: TotalCardCounts[]; + + alertsPerformanceMetrics717: PerformanceMetric717[]; + + columns: TableColumn[]; + dataAlertsPerformanceOverview: AlertsPerformanceOverviewMetricsTableData[]; + paginatedDataAlertsPerformanceOverview: AlertsPerformanceOverviewMetricsTableData[]; + columnRules: { [key: string]: number }; + order: Maybe; + onOrderBy: (columnValue: string) => void; + searchTerm: string; + setSearchTerm: React.Dispatch>; + filtersConfig: FiltersConfig[]; + filters: FiltersValuesType; + setFilters: Dispatch>; + filterOptions: (column: string) => { value: string; label: string }[]; + totalPages: number; + currentPage: number; + goToPage: (event: React.ChangeEvent, page: number) => void; +}; + +export const AlertsDashboard: React.FC = React.memo(props => { + const { + selectorFiltersConfig, + singleSelectFilters, + setSingleSelectFilters, + multiSelectFilters, + setMultiSelectFilters, + dateRangeFilter, + cardCountsLoading, + cardCounts, + alertsPerformanceMetrics717, + dataAlertsPerformanceOverview, + paginatedDataAlertsPerformanceOverview, + totalPages, + currentPage, + goToPage, + ...restAlertsPerformanceOverview + } = props; + + return ( + <> +
+ + {selectorFiltersConfig.map(({ id, label, placeholder, options, type }) => { + return ( + + {type === "multiselector" ? ( + + setMultiSelectFilters(id, values) + } + /> + ) : ( + + setSingleSelectFilters(id, value) + } + allowClear + /> + )} + + ); + })} + + + + + + + {cardCounts.map((cardCount, index) => ( + + ))} + + +
+
+ +
+
+ + {alertsPerformanceMetrics717.map( + (perfMetric717: PerformanceMetric717, index: number) => ( + + ) + )} + +
+
+ + + + +
+ + ); +}); + +const GridWrapper = styled.div` + width: 100%; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 10px; +`; + +const StyledStatsCard = styled(StatsCard)` + width: 220px; +`; + +const StatisticTableWrapper = styled.div` + display: grid; + row-gap: 16px; +`; + +const FiltersContainer = styled.div` + display: flex; + align-items: center; + flex-wrap: wrap; + margin-bottom: 1rem; + gap: 1rem; +`; + +const FilterContainer = styled.div` + display: flex; + width: 250px; + max-width: 250px; + justify-content: flex-end; + @media (max-width: 700px) { + flex-wrap: wrap; + justify-content: flex-start; + width: 100%; + } +`; diff --git a/src/webapp/pages/dashboard/DashboardPage.tsx b/src/webapp/pages/dashboard/DashboardPage.tsx index 43b55bfa..f5583294 100644 --- a/src/webapp/pages/dashboard/DashboardPage.tsx +++ b/src/webapp/pages/dashboard/DashboardPage.tsx @@ -1,25 +1,21 @@ import React, { useEffect } from "react"; +import styled from "styled-components"; +import { Tab, Tabs } from "@material-ui/core"; import i18n from "../../../utils/i18n"; import { Layout } from "../../components/layout/Layout"; -import { Section } from "../../components/section/Section"; -import { StatisticTable } from "../../components/table/statistic-table/StatisticTable"; import { useNationalPerformanceOverview } from "./useNationalPerformanceOverview"; import { useCardCounts } from "./useCardCounts"; import { StatsCard } from "../../components/stats-card/StatsCard"; -import styled from "styled-components"; -import { MultipleSelector } from "../../components/selector/MultipleSelector"; import { useCurrentEventTracker } from "../../contexts/current-event-tracker-context"; import { useAlertsActiveVerifiedFilters } from "./useAlertsActiveVerifiedFilters"; -import { MapSection } from "../../components/map/MapSection"; -import { Selector } from "../../components/selector/Selector"; -import { DateRangePicker } from "../../components/date-picker/DateRangePicker"; -import { PerformanceMetric717, use717Performance } from "./use717Performance"; +import { use717Performance } from "./use717Performance"; import { Loader } from "../../components/loader/Loader"; import { useLastAnalyticsRuntime } from "../../hooks/useLastAnalyticsRuntime"; -import LoaderContainer from "../../components/loader/LoaderContainer"; import { useAlertsPerformanceOverview } from "./useAlertsPerformanceOverview"; -import { Pagination } from "../../components/pagination/Pagination"; +import { useTabs } from "./useTabs"; +import { AlertsDashboard } from "./AlertsDashboard"; +import { NationalDashboard } from "./NationalDashboard"; export const DashboardPage: React.FC = React.memo(() => { const { @@ -65,6 +61,8 @@ export const DashboardPage: React.FC = React.memo(() => { const { resetCurrentEventTracker: resetCurrentEventTrackerId } = useCurrentEventTracker(); const { lastAnalyticsRuntime } = useLastAnalyticsRuntime(); + const { tabIndexSelected, handleTabChange } = useTabs(); + useEffect(() => { //On navigating to the dashboard page, reset the current event tracker id resetCurrentEventTrackerId(); @@ -81,125 +79,42 @@ export const DashboardPage: React.FC = React.memo(() => { showCreateEvent lastAnalyticsRuntime={lastAnalyticsRuntime} > -
- - {selectorFiltersConfig.map(({ id, label, placeholder, options, type }) => { - return ( - - {type === "multiselector" ? ( - - setMultiSelectFilters(id, values) - } - /> - ) : ( - - setSingleSelectFilters(id, value) - } - allowClear - /> - )} - - ); - })} - - - - - - - {cardCounts.map((cardCount, index) => ( - - ))} - - -
-
- + + + + + + + + {tabIndexSelected === 0 ? ( + + ) : null} + + {tabIndexSelected === 1 ? ( + -
-
- - {alertsPerformanceMetrics717.map( - (perfMetric717: PerformanceMetric717, index: number) => ( - - ) - )} - -
-
- - - - -
-
- - {nationalPerformanceMetrics717.map( - (perfMetric717: PerformanceMetric717, index: number) => ( - - ) - )} - -
-
- - - -
+ ) : null} ); }); @@ -215,27 +130,6 @@ export const StyledStatsCard = styled(StatsCard)` width: 220px; `; -const StatisticTableWrapper = styled.div` - display: grid; - row-gap: 16px; -`; - -const FiltersContainer = styled.div` - display: flex; - align-items: center; - flex-wrap: wrap; - margin-bottom: 1rem; - gap: 1rem; -`; - -const FilterContainer = styled.div` - display: flex; - width: 250px; - max-width: 250px; - justify-content: flex-end; - @media (max-width: 700px) { - flex-wrap: wrap; - justify-content: flex-start; - width: 100%; - } +const TabsContainer = styled.div` + margin-block-end: 30px; `; diff --git a/src/webapp/pages/dashboard/NationalDashboard.tsx b/src/webapp/pages/dashboard/NationalDashboard.tsx new file mode 100644 index 00000000..df79f273 --- /dev/null +++ b/src/webapp/pages/dashboard/NationalDashboard.tsx @@ -0,0 +1,88 @@ +import React, { Dispatch, SetStateAction } from "react"; +import styled from "styled-components"; + +import i18n from "../../../utils/i18n"; +import { StatsCard } from "../../components/stats-card/StatsCard"; +import { Section } from "../../components/section/Section"; +import { + FiltersConfig, + FiltersValuesType, + StatisticTable, + TableColumn, +} from "../../components/table/statistic-table/StatisticTable"; +import { PerformanceMetric717 } from "./use717Performance"; +import { Order, PerformanceOverviewMetricsTableData } from "./useNationalPerformanceOverview"; +import { Maybe } from "../../../utils/ts-utils"; + +export type NationalDashboardProps = { + nationalPerformanceMetrics717: PerformanceMetric717[]; + + columns: TableColumn[]; + dataNationalPerformanceOverview: PerformanceOverviewMetricsTableData[]; + editRiskAssessmentColumns: string[]; + columnRules: { [key: string]: number }; + order: Maybe; + onOrderBy: (columnValue: string) => void; + searchTerm: string; + setSearchTerm: React.Dispatch>; + filtersConfig: FiltersConfig[]; + filters: FiltersValuesType; + setFilters: Dispatch>; + filterOptions: (column: string) => { value: string; label: string }[]; + totalPages: number; + currentPage: number; + goToPage: (event: React.ChangeEvent, page: number) => void; + allowGoToEventOnClick: true; +}; + +export const NationalDashboard: React.FC = React.memo(props => { + const { + nationalPerformanceMetrics717, + dataNationalPerformanceOverview, + editRiskAssessmentColumns, + ...restNationalPerformanceOverview + } = props; + + return ( + <> +
+ + {nationalPerformanceMetrics717.map( + (perfMetric717: PerformanceMetric717, index: number) => ( + + ) + )} + +
+
+ + + +
+ + ); +}); + +const GridWrapper = styled.div` + width: 100%; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 10px; +`; + +const StatisticTableWrapper = styled.div` + display: grid; + row-gap: 16px; +`; diff --git a/src/webapp/pages/dashboard/useAlertsPerformanceOverview.ts b/src/webapp/pages/dashboard/useAlertsPerformanceOverview.ts index b7f2a7c5..5b1413f2 100644 --- a/src/webapp/pages/dashboard/useAlertsPerformanceOverview.ts +++ b/src/webapp/pages/dashboard/useAlertsPerformanceOverview.ts @@ -15,7 +15,7 @@ import { usePerformanceOverviewTable } from "./usePerformanceOverviewTable"; import { OrgUnitLevelType } from "../../../domain/entities/OrgUnit"; import i18n from "../../../utils/i18n"; -type AlertsPerformanceOverviewMetricsTableData = { +export type AlertsPerformanceOverviewMetricsTableData = { event: string; teiId: Id; eventEBSId: Id; @@ -82,7 +82,7 @@ export function useAlertsPerformanceOverview(): State { () => [ { value: "event", label: i18n.t("Disease - Hazard type"), type: "multiselector" }, { value: "province", label: i18n.t("Province"), type: "multiselector" }, - { value: "date", label: i18n.t("Date"), type: "datepicker" }, + { value: "date", label: i18n.t("Duration"), type: "datepicker" }, ], [] ); diff --git a/src/webapp/pages/dashboard/useNationalPerformanceOverview.ts b/src/webapp/pages/dashboard/useNationalPerformanceOverview.ts index 35635d2e..f7504ee0 100644 --- a/src/webapp/pages/dashboard/useNationalPerformanceOverview.ts +++ b/src/webapp/pages/dashboard/useNationalPerformanceOverview.ts @@ -80,7 +80,7 @@ export function useNationalPerformanceOverview(): State { () => [ { value: "event", label: i18n.t("Event"), type: "multiselector" }, { value: "province", label: i18n.t("Province"), type: "multiselector" }, - { value: "date", label: i18n.t("Date"), type: "datepicker" }, + { value: "date", label: i18n.t("Duration"), type: "datepicker" }, ], [] ); diff --git a/src/webapp/pages/dashboard/useTabs.ts b/src/webapp/pages/dashboard/useTabs.ts new file mode 100644 index 00000000..14f9a553 --- /dev/null +++ b/src/webapp/pages/dashboard/useTabs.ts @@ -0,0 +1,22 @@ +import { useCallback, useState } from "react"; + +export type State = { + tabIndexSelected: number; + handleTabChange: (event: React.ChangeEvent<{}>, newTabIndexSelected: number) => void; +}; + +export function useTabs(): State { + const [tabIndexSelected, setTabIndexSelected] = useState(0); + + const handleTabChange = useCallback( + (_event: React.ChangeEvent<{}>, newTabIndexSelected: number) => { + setTabIndexSelected(newTabIndexSelected); + }, + [] + ); + + return { + tabIndexSelected, + handleTabChange, + }; +}