diff --git a/i18n/en.pot b/i18n/en.pot index fa8e4bd7..a3deb43c 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: 2025-11-17T22:16:03.463Z\n" -"PO-Revision-Date: 2025-11-17T22:16:03.464Z\n" +"POT-Creation-Date: 2025-11-26T13:10:18.388Z\n" +"PO-Revision-Date: 2025-11-26T13:10:18.389Z\n" msgid "Failed to load: {{error}}" msgstr "Failed to load: {{error}}" @@ -911,68 +911,3 @@ msgstr "Clean idle jobs after (in milliseconds)" msgid "Use centroids for organisation unit polygons in event analytics" msgstr "Use centroids for organisation unit polygons in event analytics" - -msgid "" -"When enabled, event analytics requests that normally return polygons for " -"organisation units will return centroids instead. Improves performance for " -"large datasets." -msgstr "" -"When enabled, event analytics requests that normally return polygons for " -"organisation units will return centroids instead. Improves performance for " -"large datasets." - -msgctxt "Application title" -msgid "__MANIFEST_APP_TITLE" -msgstr "System Settings" - -msgctxt "Application description" -msgid "__MANIFEST_APP_DESCRIPTION" -msgstr "" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_General" -msgstr "General" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Analytics" -msgstr "Analytics" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Server" -msgstr "Server" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Appearance" -msgstr "Appearance" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Email" -msgstr "Email" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Access" -msgstr "Access" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Calendar" -msgstr "Calendar" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Data import settings" -msgstr "Data import settings" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Synchronization" -msgstr "Synchronization" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Scheduled job settings" -msgstr "Scheduled job settings" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_Notification settings" -msgstr "Notification settings" - -msgctxt "Title for shortcut used by command palette" -msgid "__MANIFEST_SHORTCUT_OAuth2 clients" -msgstr "OAuth2 clients" diff --git a/src/period-types/PeriodTypes.component.jsx b/src/period-types/PeriodTypes.component.jsx new file mode 100644 index 00000000..87cda6bf --- /dev/null +++ b/src/period-types/PeriodTypes.component.jsx @@ -0,0 +1,199 @@ +import { useDataQuery } from '@dhis2/app-runtime' +import i18n from '@dhis2/d2-i18n' +import { CenteredContent, CircularLoader } from '@dhis2/ui' +import CheckboxMaterial from 'material-ui/Checkbox' +import React from 'react' +import styles from './PeriodTypes.module.css' + +const query = { + periodTypes: { + resource: 'periodTypes', + }, + dataOutputPeriodTypes: { + resource: 'configuration/dataOutputPeriodTypes', + }, +} + +// Format period type names for display +const formatPeriodTypeName = (name) => { + // Handle Weekly variants + if (name === 'Weekly') { + return i18n.t('Weekly') + } + if (name.startsWith('Weekly')) { + const day = name.replace('Weekly', '') + return i18n.t('Weekly (start {{day}})', { day }) + } + + // Handle Financial year variants + if (name.startsWith('Financial')) { + const monthAbbrev = name.replace('Financial', '') + const monthMap = { + April: 'April', + July: 'July', + Oct: 'October', + Nov: 'November', + } + const month = monthMap[monthAbbrev] || monthAbbrev + return i18n.t('Financial year (start {{month}})', { month }) + } + + // Handle Six-monthly variants + if (name === 'SixMonthly') { + return i18n.t('Six-monthly') + } + if (name.startsWith('SixMonthly')) { + const monthAbbrev = name.replace('SixMonthly', '') + const monthMap = { + April: 'April', + Nov: 'November', + } + const month = monthMap[monthAbbrev] || monthAbbrev + return i18n.t('Six-monthly (start {{month}})', { month }) + } + + // Handle Quarterly variants + if (name === 'Quarterly') { + return i18n.t('Quarterly') + } + if (name.startsWith('Quarterly')) { + const monthAbbrev = name.replace('Quarterly', '') + const monthMap = { + Nov: 'November', + } + const month = monthMap[monthAbbrev] || monthAbbrev + return i18n.t('Quarterly (start {{month}})', { month }) + } + + // Handle other common types + const simpleLabels = { + Daily: i18n.t('Daily'), + Monthly: i18n.t('Monthly'), + BiMonthly: i18n.t('Bi-monthly'), + Yearly: i18n.t('Yearly'), + BiWeekly: i18n.t('Bi-weekly'), + } + + if (simpleLabels[name]) { + return simpleLabels[name] + } + + // Fallback: add spaces before capital letters + return name.replace(/([A-Z])/g, ' $1').trim() +} + +// Map frequencyOrder to group labels +const getGroupLabel = (frequencyOrder) => { + if (frequencyOrder === 1) { + return i18n.t('Days') + } + if (frequencyOrder === 7) { + return i18n.t('Weeks') + } + if (frequencyOrder === 14) { + return i18n.t('Bi-weeks') + } + if (frequencyOrder === 30) { + return i18n.t('Months') + } + if (frequencyOrder === 60) { + return i18n.t('Bi-months') + } + if (frequencyOrder === 91) { + return i18n.t('Quarters') + } + if (frequencyOrder === 182) { + return i18n.t('Six months') + } + if (frequencyOrder === 365) { + return i18n.t('Years') + } + return i18n.t('Other') +} + +// Group period types by frequencyOrder +const groupByFrequency = (periodTypes) => { + const groups = {} + periodTypes.forEach((pt) => { + const freq = pt.frequencyOrder + if (!groups[freq]) { + groups[freq] = { + label: getGroupLabel(freq), + frequencyOrder: freq, + periodTypes: [], + } + } + groups[freq].periodTypes.push(pt) + }) + // Sort groups by frequencyOrder and return as array + return Object.values(groups).sort( + (a, b) => a.frequencyOrder - b.frequencyOrder + ) +} + +const PeriodTypes = () => { + const { loading, error, data } = useDataQuery(query) + + if (loading) { + return ( + + + + ) + } + + if (error) { + return ( +
+ {i18n.t('Failed to load period types: {{error}}', { + error: error.message, + nsSeparator: '-:-', + })} +
+ ) + } + + const allPeriodTypes = data?.periodTypes?.periodTypes || [] + const allowedPeriodTypes = data?.dataOutputPeriodTypes || [] + + // Create a Set of allowed period type names for quick lookup + const allowedSet = new Set(allowedPeriodTypes.map((pt) => pt.name)) + + // Group period types by frequency + const groupedPeriodTypes = groupByFrequency(allPeriodTypes) + + return ( +
+

+ {i18n.t('Period types available in analytics apps')} +

+
+ {groupedPeriodTypes.map((group) => ( +
+

{group.label}

+
+ {group.periodTypes.map((periodType) => ( +
+ {}} + /> +
+ ))} +
+
+ ))} +
+
+ ) +} + +export default PeriodTypes diff --git a/src/period-types/PeriodTypes.module.css b/src/period-types/PeriodTypes.module.css new file mode 100644 index 00000000..32f58b6c --- /dev/null +++ b/src/period-types/PeriodTypes.module.css @@ -0,0 +1,45 @@ +.wrapper { + margin: 16px 0; +} + +.sectionLabel { + color: var(--colors-grey800); + font-size: 12px; + margin: 0; +} + +.groupsWrapper { + margin: 8px 0; + padding: 12px; + background: var(--colors-grey050); + border: 1px solid var(--colors-grey200); + border-radius: 7px; + display: flex; + flex-direction: column; + gap: 24px; +} + +.groupLabel { + font-size: 13px; + font-weight: 500; + color: var(--colors-grey600); + margin: 0px 0 8px 0; +} + +.checkboxList { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 6px 24px; + padding-left: 8px; + width: 100%; +} + +.checkboxItem { + min-width: 0; + font-size: 15px; +} + +.error { + color: var(--colors-red600); + padding: 16px; +} diff --git a/src/settingsCategories.js b/src/settingsCategories.js index f8e7fee3..42853279 100644 --- a/src/settingsCategories.js +++ b/src/settingsCategories.js @@ -85,19 +85,7 @@ export const categories = { setting: 'keyAnalysisDigitGroupSeparator', }, { - setting: 'keyHideDailyPeriods', - }, - { - setting: 'keyHideWeeklyPeriods', - }, - { - setting: 'keyHideBiWeeklyPeriods', - }, - { - setting: 'keyHideMonthlyPeriods', - }, - { - setting: 'keyHideBiMonthlyPeriods', + setting: 'periodTypes', }, { setting: 'analyticsFinancialYearStart', diff --git a/src/settingsFields.component.jsx b/src/settingsFields.component.jsx index 5ba00b6a..3fa8eaaa 100644 --- a/src/settingsFields.component.jsx +++ b/src/settingsFields.component.jsx @@ -20,6 +20,7 @@ import LocalizedAppearance from './localized-text/LocalizedAppearanceEditor.comp import metadataSettings from './metadata-settings/metadataSettings.component.jsx' import Oauth2ClientEditor from './oauth2-client-editor/OAuth2ClientEditor.component.jsx' import Oauth2ClientEditor41 from './oauth2-client-editor-41/OAuth2ClientEditor.component.jsx' +import PeriodTypes from './period-types/PeriodTypes.component.jsx' import settingsActions from './settingsActions.js' import { categories } from './settingsCategories.js' import classes from './SettingsFields.module.css' @@ -306,6 +307,11 @@ class SettingsFields extends React.Component { component: metadataSettings, }) + case 'periodTypes': + return Object.assign({}, fieldBase, { + component: PeriodTypes, + }) + default: console.warn( `Unknown control type "${mapping.type}" encountered for field "${key}"` diff --git a/src/settingsKeyMapping.js b/src/settingsKeyMapping.js index 95c08564..6ee9b8e6 100644 --- a/src/settingsKeyMapping.js +++ b/src/settingsKeyMapping.js @@ -177,26 +177,9 @@ const settingsKeyMapping = { LAST_5_FINANCIAL_YEARS: i18n.t('Last 5 financial years'), }, }, - keyHideDailyPeriods: { - label: i18n.t('Hide daily periods'), - sectionLabel: i18n.t('Hidden period types in analytics apps'), - type: 'checkbox', - }, - keyHideWeeklyPeriods: { - label: i18n.t('Hide weekly periods'), - type: 'checkbox', - }, - keyHideBiWeeklyPeriods: { - label: i18n.t('Hide biweekly periods'), - type: 'checkbox', - }, - keyHideMonthlyPeriods: { - label: i18n.t('Hide monthly periods'), - type: 'checkbox', - }, - keyHideBiMonthlyPeriods: { - label: i18n.t('Hide bimonthly periods'), - type: 'checkbox', + periodTypes: { + type: 'periodTypes', + searchLabels: [i18n.t('Period types'), i18n.t('Allowed period types')], }, analyticsFinancialYearStart: { label: i18n.t('Financial year relative period start month'),