diff --git a/i18n/en.pot b/i18n/en.pot index 73d40913..1b004852 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: 2022-01-10T08:43:46.954Z\n" -"PO-Revision-Date: 2022-01-10T08:43:46.954Z\n" +"POT-Creation-Date: 2022-08-23T08:55:03.041Z\n" +"PO-Revision-Date: 2022-08-23T08:55:03.041Z\n" msgid "" msgstr "" @@ -77,10 +77,10 @@ msgstr "" msgid "NHWA Data Approval Status Report" msgstr "" -msgid "Data set" +msgid "Period" msgstr "" -msgid "Period" +msgid "Data set" msgstr "" msgid "Attribute" @@ -128,6 +128,12 @@ msgstr "" msgid "NHWA Comments Report" msgstr "" +msgid "Download CSV" +msgstr "" + +msgid "Switch Periods" +msgstr "" + msgid "Section" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index a81ba594..5d178801 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2022-01-10T08:43:46.954Z\n" +"POT-Creation-Date: 2022-08-23T08:55:03.041Z\n" "PO-Revision-Date: 2018-10-25T09:02:35.143Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -77,10 +77,10 @@ msgstr "" msgid "NHWA Data Approval Status Report" msgstr "" -msgid "Data set" +msgid "Period" msgstr "" -msgid "Period" +msgid "Data set" msgstr "" msgid "Attribute" @@ -128,6 +128,12 @@ msgstr "" msgid "NHWA Comments Report" msgstr "" +msgid "Download CSV" +msgstr "" + +msgid "Switch Periods" +msgstr "" + msgid "Section" msgstr "" diff --git a/src/data/NHWADataCommentsDefaultRepository.ts b/src/data/NHWADataCommentsDefaultRepository.ts index 5c627ff5..2e63c8a0 100644 --- a/src/data/NHWADataCommentsDefaultRepository.ts +++ b/src/data/NHWADataCommentsDefaultRepository.ts @@ -63,27 +63,41 @@ export class NHWADataCommentsDefaultRepository implements NHWADataCommentsReposi .join("-") || "-"; const sqlViews = new Dhis2SqlViews(this.api); - const { pager, rows } = await sqlViews - .query( - config.dataCommentsSqlView.id, - { - orgUnitIds: sqlViewJoinIds(orgUnitIds), - periods: sqlViewJoinIds(_.isEmpty(periods) ? config.years : periods), - dataSetIds: sqlViewJoinIds(dataSetIds2), - sectionIds: sqlViewJoinIds(sectionIds), - orderByColumn: fieldMapping[sorting.field], - orderByDirection: sorting.direction, - commentPairs, - }, - paging - ) - .getData(); + const { pager, rows } = _.merge( + await sqlViews + .query( + config.dataCommentsSqlView.id, + { + orgUnitIds: sqlViewJoinIds(orgUnitIds), + periods: sqlViewJoinIds(periods), + dataSetIds: sqlViewJoinIds(dataSetIds2), + sectionIds: sqlViewJoinIds(sectionIds), + orderByColumn: fieldMapping[sorting.field], + orderByDirection: sorting.direction, + commentPairs, + }, + paging + ) + .getData() + ); // A data value is not associated to a specific data set, but we can still map it // through the data element (1 data value -> 1 data element -> N data sets). const dataValues: Array = rows.map( - (dv): DataCommentsItem => ({ + (dv: { + period: string; + orgunit: any; + datasetname: any; + dataelementid: any; + dataelementname: any; + section: any; + cocname: any; + value: any; + comment: any; + lastupdated: string | number | Date; + storedby: any; + }): DataCommentsItem => ({ period: dv.period.split("-")[0] ?? "", orgUnit: { name: dv.orgunit }, dataSet: { name: dv.datasetname }, diff --git a/src/domain/nhwa-comments/usecases/GetDataValuesUseCase.ts b/src/domain/nhwa-comments/usecases/GetDataValuesUseCase.ts index 171d77f1..5a1c31c0 100644 --- a/src/domain/nhwa-comments/usecases/GetDataValuesUseCase.ts +++ b/src/domain/nhwa-comments/usecases/GetDataValuesUseCase.ts @@ -5,7 +5,7 @@ import { import { DataCommentsItem } from "../entities/DataCommentsItem"; import { PaginatedObjects } from "../../common/entities/PaginatedObjects"; -type GetDataValuesUseCaseOptions = NHWADataCommentsRepositoryGetOptions; +export type GetDataValuesUseCaseOptions = NHWADataCommentsRepositoryGetOptions; export class GetDataValuesUseCase { constructor(private dataValueRepository: NHWADataCommentsRepository) {} diff --git a/src/webapp/reports/nhwa-comments/data-comments-list/DataCommentsList.tsx b/src/webapp/reports/nhwa-comments/data-comments-list/DataCommentsList.tsx index abaeeb62..0f7d3e06 100644 --- a/src/webapp/reports/nhwa-comments/data-comments-list/DataCommentsList.tsx +++ b/src/webapp/reports/nhwa-comments/data-comments-list/DataCommentsList.tsx @@ -6,6 +6,7 @@ import { TableSorting, } from "@eyeseetea/d2-ui-components"; import StorageIcon from "@material-ui/icons/Storage"; +import RestartAltIcon from "@material-ui/icons/Storage"; import _ from "lodash"; import React from "react"; import { sortByName } from "../../../../domain/common/entities/Base"; @@ -21,80 +22,93 @@ import { useSnackbarOnError } from "../../../utils/snackbar"; import { DataCommentsViewModel, getDataCommentsViews } from "../DataCommentsViewModel"; import { DataValuesFilter } from "./Filters"; import { FiltersBox } from "./FiltersBox"; - -export const DataCommentsList: React.FC = React.memo(() => { +export const DataCommentsList: React.FC = () => { + const [oldYears, setOldYears] = React.useState(false); const { compositionRoot, config } = useAppContext(); + + const selectablePeriods = React.useMemo(() => { + const currentYear = new Date().getFullYear(); + return oldYears + ? _.range(currentYear - 40, currentYear - 10).map(n => n.toString()) + : _.range(currentYear - 10, currentYear + 1).map(n => n.toString()); + }, [oldYears]); + const [filters, setFilters] = React.useState(() => getEmptyDataValuesFilter(config)); const baseConfig = React.useMemo(getBaseListConfig, []); const [sorting, setSorting] = React.useState>(); - const getRows = React.useMemo( () => async (paging: TablePagination, sorting: TableSorting) => { const { pager, objects } = await compositionRoot.dataComments.get({ config, paging: { page: paging.page, pageSize: paging.pageSize }, sorting: getSortingFromTableSorting(sorting), - ...getUseCaseOptions(filters), + ...getUseCaseOptions(filters, selectablePeriods), }); + setFilters(filters); setSorting(sorting); return { pager, objects: getDataCommentsViews(config, objects) }; }, - [config, compositionRoot, filters] + [config, compositionRoot, filters, selectablePeriods] ); - const getRowsWithSnackbarOrError = useSnackbarOnError(getRows); const tableProps = useObjectsTable(baseConfig, getRowsWithSnackbarOrError); - const filterOptions = React.useMemo(() => getFilterOptions(config, filters), [config, filters]); - + const filterOptions = React.useMemo( + () => getFilterOptions(config, filters, selectablePeriods), + [config, filters, selectablePeriods] + ); const downloadCsv: TableGlobalAction = { name: "downloadCsv", - text: "Download CSV", + text: i18n.t("Download CSV"), icon: , onClick: async () => { if (!sorting) return; - // FUTURE: create a single use case that performs the get+saveCSV const { objects: dataValues } = await compositionRoot.dataComments.get({ config, paging: { page: 1, pageSize: 100000 }, sorting: getSortingFromTableSorting(sorting), - ...getUseCaseOptions(filters), + ...getUseCaseOptions(filters, selectablePeriods), }); compositionRoot.dataComments.save("data-values.csv", dataValues); }, }; - + const periodsToggle: TableGlobalAction = { + name: "switchPeriods", + text: i18n.t("Switch Periods"), + icon: , + onClick: async () => { + setOldYears(oldYears => !oldYears); + setFilters(currentFilters => ({ ...currentFilters, periods: [] })); + }, + }; return ( - {...tableProps} globalActions={[downloadCsv]}> + {...tableProps} globalActions={[downloadCsv, periodsToggle]}> ); -}); +}; -function getUseCaseOptions(filter: DataValuesFilter) { +function getUseCaseOptions(filter: DataValuesFilter, selectablePeriods: string[]) { return { ...filter, + periods: _.isEmpty(filter.periods) ? selectablePeriods : filter.periods, orgUnitIds: getOrgUnitIdsFromPaths(filter.orgUnitPaths), }; } - function getSortingFromTableSorting(sorting: TableSorting): Sorting { return { field: sorting.field === "id" ? "period" : sorting.field, direction: sorting.order, }; } - function getBaseListConfig(): TableConfig { const paginationOptions: PaginationOptions = { pageSizeOptions: [10, 20, 50], pageSizeInitialValue: 10, }; - const initialSorting: TableSorting = { field: "dataSet" as const, order: "asc" as const, }; - const columns: TableColumn[] = [ { name: "dataSet", text: i18n.t("Data set"), sortable: true }, { name: "period", text: i18n.t("Period"), sortable: true }, @@ -107,11 +121,9 @@ function getBaseListConfig(): TableConfig { { name: "lastUpdated", text: i18n.t("Last updated"), sortable: true, hidden: true }, { name: "storedBy", text: i18n.t("Stored by"), sortable: true, hidden: true }, ]; - return { columns, initialSorting, paginationOptions }; } - -function getFilterOptions(config: Config, filters: DataValuesFilter) { +function getFilterOptions(config: Config, filters: DataValuesFilter, selectablePeriods: string[]) { const { dataSetIds } = filters; const sections = _(config.sectionsByDataSet) .at(_.isEmpty(dataSetIds) ? _.keys(config.sectionsByDataSet) : dataSetIds) @@ -119,14 +131,12 @@ function getFilterOptions(config: Config, filters: DataValuesFilter) { .compact() .uniqBy(section => section.id) .value(); - return { - periods: config.years, + periods: selectablePeriods, dataSets: sortByName(_.values(config.dataSets)), sections: sortByName(sections), }; } - function getEmptyDataValuesFilter(config: Config): DataValuesFilter { return { orgUnitPaths: getMainUserPaths(config),