-
Notifications
You must be signed in to change notification settings - Fork 15
feat: enabling and disabling of data output period types #1452
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
6f589bc
6f21a22
76046b4
58bfb97
ed41269
840a08c
f88ea63
81e98d6
1f4b23e
3ad92b4
747f95e
8b69810
fa6a22d
4d0e657
9cf2556
e161030
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| { | ||
| "name": "settings-app", | ||
| "version": "100.10.0", | ||
| "version": "100.11.0", | ||
| "description": "", | ||
| "license": "BSD-3-Clause", | ||
| "private": true, | ||
|
|
@@ -34,5 +34,6 @@ | |
| "material-ui": "0.20.2", | ||
| "prop-types": "^15.7.2", | ||
| "rxjs": "5.5.7" | ||
| } | ||
| }, | ||
| "packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| import { useDataQuery } from '@dhis2/app-runtime' | ||
| import i18n from '@dhis2/d2-i18n' | ||
| import { CenteredContent, CircularLoader } from '@dhis2/ui' | ||
| import { getInstance as getD2 } from 'd2' | ||
| import CheckboxMaterial from 'material-ui/Checkbox' | ||
| import React, { useState, useCallback } from 'react' | ||
| import settingsActions from '../settingsActions.js' | ||
| import styles from './PeriodTypes.module.css' | ||
|
|
||
| const query = { | ||
| periodTypes: { | ||
| resource: 'periodTypes', | ||
| }, | ||
| dataOutputPeriodTypes: { | ||
| resource: 'configuration/dataOutputPeriodTypes', | ||
| }, | ||
| } | ||
|
|
||
| const formatPeriodTypeName = (name) => { | ||
| const simpleLabels = { | ||
| Daily: i18n.t('Daily'), | ||
| Weekly: i18n.t('Weekly'), | ||
| Monthly: i18n.t('Monthly'), | ||
| BiMonthly: i18n.t('Bi-monthly'), | ||
| Yearly: i18n.t('Yearly'), | ||
| BiWeekly: i18n.t('Bi-weekly'), | ||
| Quarterly: i18n.t('Quarterly'), | ||
| SixMonthly: i18n.t('Six-monthly'), | ||
| } | ||
|
|
||
| if (simpleLabels[name]) { | ||
| return simpleLabels[name] | ||
| } | ||
|
|
||
| const monthMap = { | ||
| April: 'April', | ||
| July: 'July', | ||
| Oct: 'October', | ||
| Nov: 'November', | ||
| } | ||
Chisomchima marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if (name.startsWith('Weekly')) { | ||
| const day = name.replace('Weekly', '') | ||
| return day | ||
|
||
| ? i18n.t('Weekly (start {{day}})', { day }) | ||
| : simpleLabels.Weekly | ||
| } | ||
|
|
||
| if (name.startsWith('Financial')) { | ||
| const monthAbbrev = name.replace('Financial', '') | ||
| const month = monthMap[monthAbbrev] || monthAbbrev | ||
| return i18n.t('Financial year (start {{month}})', { month }) | ||
| } | ||
|
|
||
| if (name.startsWith('SixMonthly')) { | ||
| const monthAbbrev = name.replace('SixMonthly', '') | ||
| const month = monthMap[monthAbbrev] || monthAbbrev | ||
| return monthAbbrev | ||
| ? i18n.t('Six-monthly (start {{month}})', { month }) | ||
| : simpleLabels.SixMonthly | ||
| } | ||
|
|
||
| if (name.startsWith('Quarterly')) { | ||
| const monthAbbrev = name.replace('Quarterly', '') | ||
| const month = monthMap[monthAbbrev] || monthAbbrev | ||
| return monthAbbrev | ||
| ? i18n.t('Quarterly (start {{month}})', { month }) | ||
| : simpleLabels.Quarterly | ||
| } | ||
|
|
||
| return name | ||
| .split(/(?=[A-Z])/) | ||
| .join(' ') | ||
| .trim() | ||
| } | ||
|
|
||
| const getGroupLabel = (frequencyOrder) => { | ||
| const labels = { | ||
| 1: i18n.t('Days'), | ||
| 7: i18n.t('Weeks'), | ||
| 14: i18n.t('Bi-weeks'), | ||
| 30: i18n.t('Months'), | ||
| 60: i18n.t('Bi-months'), | ||
| 91: i18n.t('Quarters'), | ||
| 182: i18n.t('Six months'), | ||
| 365: i18n.t('Years'), | ||
| } | ||
| return labels[frequencyOrder] || i18n.t('Other') | ||
| } | ||
|
|
||
| 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) | ||
| }) | ||
| return Object.values(groups).sort( | ||
| (a, b) => a.frequencyOrder - b.frequencyOrder | ||
| ) | ||
| } | ||
|
|
||
| const PeriodTypes = () => { | ||
| const { loading, data, refetch } = useDataQuery(query) | ||
| const [updating, setUpdating] = useState(false) | ||
|
|
||
| const handlePeriodTypeToggle = useCallback( | ||
| async (periodTypeName, isCurrentlyEnabled) => { | ||
| setUpdating(true) | ||
| try { | ||
| const d2 = await getD2() | ||
| const api = d2.Api.getApi() | ||
|
||
| const allowedPeriodTypes = data?.dataOutputPeriodTypes || [] | ||
| const currentAllowedSet = new Set( | ||
| allowedPeriodTypes.map((pt) => | ||
| typeof pt === 'string' ? pt : pt.name | ||
| ) | ||
| ) | ||
|
|
||
| if (isCurrentlyEnabled) { | ||
| currentAllowedSet.delete(periodTypeName) | ||
| } else { | ||
| currentAllowedSet.add(periodTypeName) | ||
| } | ||
|
|
||
| const updatedPeriodTypes = Array.from(currentAllowedSet).map( | ||
| (name) => ({ name }) | ||
| ) | ||
|
|
||
| await api.post( | ||
| 'configuration/dataOutputPeriodTypes', | ||
| updatedPeriodTypes | ||
| ) | ||
|
|
||
| await refetch() | ||
Chisomchima marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| settingsActions.showSnackbarMessage(i18n.t('Settings updated')) | ||
| } catch (err) { | ||
| console.error('Failed to update period types:', err) | ||
| settingsActions.showSnackbarMessage( | ||
| i18n.t( | ||
| 'There was a problem updating settings. Changes have not been saved.' | ||
| ) | ||
| ) | ||
| } finally { | ||
| setUpdating(false) | ||
| } | ||
| }, | ||
| [data, refetch] | ||
| ) | ||
|
|
||
| if (loading) { | ||
| return ( | ||
| <CenteredContent> | ||
| <CircularLoader /> | ||
| </CenteredContent> | ||
| ) | ||
| } | ||
|
|
||
| const allPeriodTypes = data?.periodTypes?.periodTypes || [] | ||
| const allowedPeriodTypes = data?.dataOutputPeriodTypes || [] | ||
| const allowedSet = new Set( | ||
| allowedPeriodTypes.map((pt) => (typeof pt === 'string' ? pt : pt.name)) | ||
| ) | ||
| const groupedPeriodTypes = groupByFrequency(allPeriodTypes) | ||
|
|
||
| return ( | ||
| <div className={styles.wrapper}> | ||
| <p className={styles.sectionLabel}> | ||
| {i18n.t('Period types available in analytics apps')} | ||
| </p> | ||
| <div className={styles.groupsWrapper}> | ||
| {groupedPeriodTypes.map((group) => ( | ||
| <div key={group.frequencyOrder} className={styles.group}> | ||
| <p className={styles.groupLabel}>{group.label}</p> | ||
| <div className={styles.checkboxList}> | ||
| {group.periodTypes.map((periodType) => { | ||
| const isEnabled = allowedSet.has( | ||
| periodType.name | ||
| ) | ||
| return ( | ||
| <div | ||
| key={periodType.name} | ||
| className={styles.checkboxItem} | ||
| > | ||
| <CheckboxMaterial | ||
| checked={isEnabled} | ||
| disabled={updating} | ||
| label={formatPeriodTypeName( | ||
| periodType.name | ||
| )} | ||
| onCheck={() => | ||
| handlePeriodTypeToggle( | ||
| periodType.name, | ||
| isEnabled | ||
| ) | ||
| } | ||
| /> | ||
| </div> | ||
| ) | ||
| })} | ||
| </div> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| ) | ||
| } | ||
|
|
||
| export default PeriodTypes | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these should be translated