diff --git a/i18n/en.pot b/i18n/en.pot index 920fbcd7..fc35dde2 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-12-17T16:04:30.663Z\n" -"PO-Revision-Date: 2024-12-17T16:04:30.663Z\n" +"POT-Creation-Date: 2025-01-27T03:20:13.064Z\n" +"PO-Revision-Date: 2025-01-27T03:20:13.064Z\n" msgid "Events - Create/update" msgstr "" @@ -752,40 +752,43 @@ msgstr "" msgid "User does not have any capture organisations units" msgstr "" -msgid "Advanced template properties" +msgid "Populate" msgstr "" -msgid "Language" +msgid "Populate template with data" msgstr "" -msgid "Split data entry tabs by section" +msgid "Also filter TEI and relationships by their enrollment date" msgstr "" -msgid "Theme" +msgid "TEI and relationships enrollment by organisation unit type" msgstr "" -msgid "" +msgid "Current user organisation units (data capture)" msgstr "" -msgid "Populate template with data" +msgid "Selected organisation units with their descendants" msgstr "" -msgid "Also filter TEI and relationships by their enrollment date" +msgid "Only selected organisation units" msgstr "" -msgid "TEI and relationships enrollment by organisation unit type" +msgid "Include relationships" msgstr "" -msgid "Current user organisation units (data capture)" +msgid "Advanced template properties" msgstr "" -msgid "Selected organisation units with their descendants" +msgid "Language" msgstr "" -msgid "Only selected organisation units" +msgid "Theme" msgstr "" -msgid "Include relationships" +msgid "" +msgstr "" + +msgid "Split data entry tabs by section" msgstr "" msgid "Use metadata codes (organisation units, data elements and options)" diff --git a/i18n/es.po b/i18n/es.po index c53b31cd..bc3c5733 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Bulk Load\n" -"POT-Creation-Date: 2024-12-17T16:04:30.663Z\n" +"POT-Creation-Date: 2025-01-27T03:20:13.064Z\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -785,20 +785,9 @@ msgstr "Seleccione las unidades organizativas para añadir a la plantilla" msgid "User does not have any capture organisations units" msgstr "El usuario no tiene unidades organizativas asociadas" -msgid "Advanced template properties" -msgstr "Propiedades avanzadas de la plantilla" - -msgid "Language" -msgstr "Idioma" - -msgid "Split data entry tabs by section" -msgstr "" - -msgid "Theme" -msgstr "Tema" - -msgid "" -msgstr "" +#, fuzzy +msgid "Populate" +msgstr "Plantilla" msgid "Populate template with data" msgstr "Rellenar la plantilla con datos" @@ -821,6 +810,21 @@ msgstr "" msgid "Include relationships" msgstr "" +msgid "Advanced template properties" +msgstr "Propiedades avanzadas de la plantilla" + +msgid "Language" +msgstr "Idioma" + +msgid "Theme" +msgstr "Tema" + +msgid "" +msgstr "" + +msgid "Split data entry tabs by section" +msgstr "" + msgid "Use metadata codes (organisation units, data elements and options)" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 6f04da23..40daff16 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Bulk Load App\n" -"POT-Creation-Date: 2024-12-17T16:04:30.663Z\n" +"POT-Creation-Date: 2025-01-27T03:20:13.064Z\n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -807,20 +807,9 @@ msgstr "" "L'utilisateur n'a pas accès à aucune unité d'organisation por la saisie de " "données" -msgid "Advanced template properties" -msgstr "Propriétés avancées du modèle" - -msgid "Language" -msgstr "Langue" - -msgid "Split data entry tabs by section" -msgstr "" - -msgid "Theme" -msgstr "Thème" - -msgid "" -msgstr "" +#, fuzzy +msgid "Populate" +msgstr "Modèle" msgid "Populate template with data" msgstr "Remplir le modèle avec des données" @@ -843,6 +832,21 @@ msgstr "" msgid "Include relationships" msgstr "" +msgid "Advanced template properties" +msgstr "Propriétés avancées du modèle" + +msgid "Language" +msgstr "Langue" + +msgid "Theme" +msgstr "Thème" + +msgid "" +msgstr "" + +msgid "Split data entry tabs by section" +msgstr "" + msgid "Use metadata codes (organisation units, data elements and options)" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index dcefb9ed..bfc7cd14 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Bulk Load\n" -"POT-Creation-Date: 2024-12-17T16:04:30.663Z\n" +"POT-Creation-Date: 2025-01-27T03:20:13.064Z\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -840,20 +840,9 @@ msgstr "Selecione as unidades organizacionais para incluir na planilha" msgid "User does not have any capture organisations units" msgstr "O usuário não tem accesso a nenhuma unidade organizacional" -msgid "Advanced template properties" -msgstr "Propriedades avançadas da planilha" - -msgid "Language" -msgstr "Língua" - -msgid "Split data entry tabs by section" -msgstr "" - -msgid "Theme" -msgstr "Tema" - -msgid "" -msgstr "" +#, fuzzy +msgid "Populate" +msgstr "Modelo" msgid "Populate template with data" msgstr "Preencher a planilha com dados" @@ -876,6 +865,21 @@ msgstr "" msgid "Include relationships" msgstr "" +msgid "Advanced template properties" +msgstr "Propriedades avançadas da planilha" + +msgid "Language" +msgstr "Língua" + +msgid "Theme" +msgstr "Tema" + +msgid "" +msgstr "" + +msgid "Split data entry tabs by section" +msgstr "" + msgid "Use metadata codes (organisation units, data elements and options)" msgstr "" diff --git a/i18n/ru.po b/i18n/ru.po index 04f48165..3ae3c9c3 100644 --- a/i18n/ru.po +++ b/i18n/ru.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Bulk Load\n" -"POT-Creation-Date: 2024-12-17T16:04:30.663Z\n" +"POT-Creation-Date: 2025-01-27T03:20:13.064Z\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -842,20 +842,9 @@ msgstr "Выберите доступные подразделения орга msgid "User does not have any capture organisations units" msgstr "У пользователя нет подразделений организаций захвата" -msgid "Advanced template properties" -msgstr "Расширенные свойства шаблона" - -msgid "Language" -msgstr "Язык" - -msgid "Split data entry tabs by section" -msgstr "" - -msgid "Theme" -msgstr "Тема" - -msgid "" -msgstr "<Нет значения>" +#, fuzzy +msgid "Populate" +msgstr "Шаблон" msgid "Populate template with data" msgstr "Заполнение шаблона данными" @@ -878,6 +867,21 @@ msgstr "" msgid "Include relationships" msgstr "" +msgid "Advanced template properties" +msgstr "Расширенные свойства шаблона" + +msgid "Language" +msgstr "Язык" + +msgid "Theme" +msgstr "Тема" + +msgid "" +msgstr "<Нет значения>" + +msgid "Split data entry tabs by section" +msgstr "" + msgid "Use metadata codes (organisation units, data elements and options)" msgstr "" diff --git a/src/webapp/components/collapsible-section/Section.tsx b/src/webapp/components/collapsible-section/Section.tsx new file mode 100644 index 00000000..65ac2299 --- /dev/null +++ b/src/webapp/components/collapsible-section/Section.tsx @@ -0,0 +1,98 @@ +import { Icon, IconButton, makeStyles, Paper } from "@material-ui/core"; +import React from "react"; +import { buildClassName } from "../../utils/reactHelpers"; + +type IconPosition = "left" | "right"; +type ArrowStyle = "up_down" | "right_left" | "down_right"; + +interface SectionProps { + isOpen?: boolean; + setOpen?: (open: boolean) => void; + children: React.ReactNode; + title: React.ReactNode; + collapsible?: boolean; + elevation?: number; + iconPos?: IconPosition; + classProps?: { + sectionPaper?: string; + sectionHeader?: string; + sectionContent?: string; + }; + arrowStyle?: ArrowStyle; +} + +export const Section = (props: SectionProps) => { + const { + children, + title, + collapsible, + isOpen = true, + setOpen = () => {}, + elevation = 1, + iconPos = "right", + classProps = {}, + arrowStyle = "up_down", + } = props; + const classes = useStyles(); + const leftIcon = iconPos === "left" ? classes.leftIcon : null; + const toggle = () => setOpen(!isOpen); + + return ( + +
+ {collapsible && leftIcon && } + {title} + {collapsible && !leftIcon && } +
+
+ {children} +
+
+ ); +}; + +interface CollapsibleToggleProps { + isOpen: boolean; + arrowStyle: ArrowStyle; +} + +const CollapsibleToggle = (props: CollapsibleToggleProps) => { + const { isOpen, arrowStyle } = props; + const classes = useStyles(); + const [open, close] = arrowStyle.split("_"); + + return ( + + {isOpen ? `keyboard_arrow_${open}` : `keyboard_arrow_${close}`} + + ); +}; + +const useStyles = makeStyles({ + header: { + margin: 0, + padding: "1em", + paddingLeft: 0, + display: "flex", + justifyContent: "space-between", + alignItems: "center", + borderBottom: "solid 1px #e8edf2", + cursor: "pointer", + }, + noBorder: { border: "none" }, + leftIcon: { + justifyContent: "normal", + gap: 10, + }, + paper: { padding: "1em", paddingTop: "0.5em", marginBottom: "1em" }, + button: { padding: 0 }, + hidden: { display: "none" }, +}); diff --git a/src/webapp/components/template-selector/TemplateSelector.tsx b/src/webapp/components/template-selector/TemplateSelector.tsx index 7d60984e..269b9962 100644 --- a/src/webapp/components/template-selector/TemplateSelector.tsx +++ b/src/webapp/components/template-selector/TemplateSelector.tsx @@ -1,6 +1,6 @@ import { Id } from "@eyeseetea/d2-api"; import { DatePicker, OrgUnitsSelector } from "@eyeseetea/d2-ui-components"; -import { Checkbox, FormControlLabel, makeStyles } from "@material-ui/core"; +import { Checkbox, FormControlLabel, Icon, makeStyles, Tooltip, Typography } from "@material-ui/core"; import _ from "lodash"; import moment from "moment"; import React, { useEffect, useMemo, useState } from "react"; @@ -15,6 +15,7 @@ import { useAppContext } from "../../contexts/app-context"; import Settings from "../../logic/settings"; import { orgUnitListParams } from "../../utils/template"; import { Select, SelectOption } from "../select/Select"; +import { Section } from "../collapsible-section/Section"; type DataSource = Record; @@ -81,6 +82,7 @@ export const TemplateSelector = ({ showLanguage: false, } ); + const [showAdvancedOptions, setShowAdvancedOptions] = useState(true); const dataSets = dataSource?.dataSets; const { templateId, id } = state; @@ -152,12 +154,12 @@ export const TemplateSelector = ({ useEffect(() => { const { type, id, templateId, templateType, ...rest } = state; if (type && id && templateId && templateType) { - const orgUnits = filterOrgUnits ? cleanOrgUnitPaths(selectedOrgUnits) : []; + const orgUnits = cleanOrgUnitPaths(selectedOrgUnits); onChange({ type, id, orgUnits, templateId, templateType, ...rest }); } else { onChange(null); } - }, [state, selectedOrgUnits, filterOrgUnits, orgUnitTreeFilter, onChange]); + }, [state, selectedOrgUnits, orgUnitTreeFilter, onChange]); const themeOptions = dataSource ? modelToSelectOption(themes) : []; @@ -211,7 +213,7 @@ export const TemplateSelector = ({ showLanguage: customTemplate?.showLanguage || false, showPeriod: customTemplate?.showPeriod || false, })); - setFilterOrgUnits(false); + clearPopulateDates(); setSelectedOrgUnits([]); } @@ -270,11 +272,7 @@ export const TemplateSelector = ({ setState(state => ({ ...state, filterTEIEnrollmentDate })); }; - const onFilterOrgUnitsChange = (_event: React.ChangeEvent, filterOrgUnits: boolean) => { - const isCustomProgram = state.templateType === "custom" && state.type !== "dataSets"; - const populate = isCustomProgram && filterOrgUnits; - setState(state => ({ ...state, populate })); - clearPopulateDates(); + const onFilterOrgUnitsChange = (filterOrgUnits: boolean) => { setFilterOrgUnits(filterOrgUnits); }; @@ -287,90 +285,114 @@ export const TemplateSelector = ({ const showPopulate = !(state.templateType === "custom" && !settings.showPopulateInCustomForms); const selected = state.id && state.templateId ? getOptionValue({ id: state.id, templateId: state.templateId }) : ""; + const subSectionClasses = useMemo(() => { + return { sectionHeader: classes.subSectionHeader, sectionPaper: classes.subSection }; + }, [classes]); + return ( <> -

{i18n.t("Template")}

- -
- {models.length > 1 && ( -
- -
-
- - {state.type === "dataSets" && state.templateType === "custom" && showPopulate && ( - onCustomFormDateChange(date)} - maxDate={state.endDate} - views={datePickerFormat?.views} - format={datePickerFormat?.format ?? "DD/MM/YYYY"} - InputLabelProps={{ style: { color: "#494949" } }} - /> - )} - - {state.type === "dataSets" && (showPopulate || state.showPeriod) && ( +
{i18n.t("Template")}} classProps={classes}>
+ {models.length > 1 && ( +
+
- )} - {settings.orgUnitSelection !== "import" && showPopulate && ( - <> -

{i18n.t("Organisation units")}

+ {state.type === "dataSets" &&

Periods

} + + {state.type === "dataSets" && state.templateType === "custom" && showPopulate && ( + onCustomFormDateChange(date)} + maxDate={state.endDate} + views={datePickerFormat?.views} + format={datePickerFormat?.format ?? "DD/MM/YYYY"} + InputLabelProps={{ style: { color: "#494949" } }} + /> + )} -
- } - label={ - state.templateType === "custom" - ? i18n.t("Select organisation unit to populate data") - : i18n.t("Select available organisation units to include in the template") - } - /> + {state.type === "dataSets" && (showPopulate || state.showPeriod) && ( +
+
+ onStartDateChange("startDate", date, true)} + maxDate={state.endDate} + views={datePickerFormat?.views} + format={datePickerFormat?.format ?? "DD/MM/YYYY"} + InputLabelProps={{ style: { color: "#494949" } }} + /> +
+
+ onEndDateChange("endDate", date, true)} + minDate={state.startDate} + views={datePickerFormat?.views} + format={datePickerFormat?.format ?? "DD/MM/YYYY"} + InputLabelProps={{ style: { color: "#494949" } }} + /> +
+ )} - {filterOrgUnits && - (!_.isEmpty(orgUnitTreeRootIds) ? ( + {settings.orgUnitSelection !== "import" && showPopulate && ( +
+

+ {i18n.t("Organisation units")} + + ({selectedOrgUnits.length}) + + + help + +

+
+ } + > + {!_.isEmpty(orgUnitTreeRootIds) ? (
{i18n.t("User does not have any capture organisations units")}
- ))} - - )} - - {state.templateType !== "custom" &&

{i18n.t("Advanced template properties")}

} - - {availableLanguages.length > 0 && (state.templateType !== "custom" || state.showLanguage) && ( -
-
- ")} - value={state.theme ?? ""} + {userHasReadAccess && !!selectedOrgUnits.length && state.templateType !== "custom" && ( +
{i18n.t("Populate")}} classProps={classes}> +
+ } + label={i18n.t("Populate template with data")} />
-
- )} - {userHasReadAccess && filterOrgUnits && state.templateType !== "custom" && ( -
- } - label={i18n.t("Populate template with data")} - /> -
- )} + {/*start/end date*/} + {state.populate && !isCustomDataSet && ( + <> + {!isDataSet && ( +
+ + } + label={i18n.t("Also filter TEI and relationships by their enrollment date")} + /> +
+ )} + +
+
+ onStartDateChange("populateStartDate", date)} + minDate={ + state.type === "dataSets" + ? state.startDate?.startOf(datePickerFormat?.unit ?? "day") + : undefined + } + maxDate={moment.min( + _.compact([ + state.type === "dataSets" && state.endDate, + state.populateEndDate, + ]) + )} + views={datePickerFormat?.views} + format={datePickerFormat?.format ?? "DD/MM/YYYY"} + InputLabelProps={{ style: { color: "#494949" } }} + /> +
+ +
+ onEndDateChange("populateEndDate", date)} + minDate={moment.max( + _.compact([ + state.type === "dataSets" && state.startDate, + state.populateStartDate, + ]) + )} + maxDate={ + state.type === "dataSets" + ? state.endDate?.endOf(datePickerFormat?.unit ?? "day") + : undefined + } + views={datePickerFormat?.views} + format={datePickerFormat?.format ?? "DD/MM/YYYY"} + InputLabelProps={{ style: { color: "#494949" } }} + /> +
+
+ + )} - {state.populate && !isCustomDataSet && ( - <> - {!isDataSet && ( + {state.populate && state.type === "trackerPrograms" && userHasReadAccess && (
- + {i18n.t("TEI and relationships enrollment by organisation unit type")} + +
+
+
- - )} + )} - {state.populate && state.type === "trackerPrograms" && userHasReadAccess && ( -
-

- {i18n.t("TEI and relationships enrollment by organisation unit type")} -

+ {themeOptions.length > 0 && (