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 (
+
+ {i18n.t('Period types available in analytics apps')} +
+{group.label}
+