Skip to content

Commit

Permalink
feat: add bottom bar functionality (DHIS2-11287) (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohammer5 authored Jul 20, 2021
1 parent b130e55 commit 1f5a77e
Show file tree
Hide file tree
Showing 31 changed files with 517 additions and 28 deletions.
54 changes: 52 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -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: 2021-07-14T10:11:31.114Z\n"
"PO-Revision-Date: 2021-07-14T10:11:31.114Z\n"
"POT-Creation-Date: 2021-07-20T08:50:33.586Z\n"
"PO-Revision-Date: 2021-07-20T08:50:33.586Z\n"

msgid "Not authorized"
msgstr "Not authorized"
Expand All @@ -18,6 +18,56 @@ msgstr ""
"You don't have access to the Data Approval App. Contact a system "
"administrator to request access."

msgid "Acceptance failed: {{error}}"
msgstr "Acceptance failed: {{error}}"

msgid "Accept"
msgstr "Accept"

msgid "Approval saved"
msgstr "Approval saved"

msgid "Approve"
msgstr "Approve"

msgid "Approving {{count}} data sets"
msgid_plural "Approving {{count}} data sets"
msgstr[0] "Approving {{count}} data sets"
msgstr[1] "Approving {{count}} data sets"

msgid "Approving {{count}} data set"
msgid_plural "Approving {{count}} data set"
msgstr[0] "Approving {{count}} data set"
msgstr[1] "Approving {{count}} data set"

msgid "Are you sure you want to approve this data?"
msgstr "Are you sure you want to approve this data?"

msgid "There was a problem saving this approval"
msgstr "There was a problem saving this approval"

msgid ""
"This data couldn’t be approved. Try again, or contact your system "
"administrator."
msgstr ""
"This data couldn’t be approved. Try again, or contact your system "
"administrator."

msgid "Cancel"
msgstr "Cancel"

msgid "Unacceptance failed: {{error}}"
msgstr "Unacceptance failed: {{error}}"

msgid "Unaccept"
msgstr "Unaccept"

msgid "Unapproval failed: {{error}}"
msgstr "Unapproval failed: {{error}}"

msgid "Unapprove"
msgstr "Unapprove"

msgid "Choose a data set to review"
msgstr "Choose a data set to review"

Expand Down
2 changes: 0 additions & 2 deletions src/app/layout.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
background-color: var(--colors-grey100);
}
.bottom {
display: flex;
align-items: center;
padding: 8px 12px;
box-shadow: 0 -2px 4px 0 rgba(0, 0, 0, 0.12);
border-top: 1px solid var(--colors-grey300);
Expand Down
37 changes: 37 additions & 0 deletions src/bottom-bar/accept-button/accept-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useAlert } from '@dhis2/app-runtime'
import i18n from '@dhis2/d2-i18n'
import { Button } from '@dhis2/ui'
import React, { useEffect } from 'react'
import { useWorkflowContext } from '../../workflow-context/index.js'
import { useAcceptData } from './use-accept-data.js'

const AcceptButton = () => {
const [acceptData, { loading, error }] = useAcceptData()
const { params, refresh } = useWorkflowContext()
const { show } = useAlert(
i18n.t('Acceptance failed: {{error}}', {
error: error ? error.toString() : '',
nsSeparator: null,
})
)

useEffect(() => {
if (error?.message) show()
}, [error?.message])

return (
<Button
small
disabled={loading}
onClick={async () => {
const { wf, pe, ou } = params
await acceptData({ wf, pe, ou })
refresh()
}}
>
{i18n.t('Accept')}
</Button>
)
}

export { AcceptButton }
1 change: 1 addition & 0 deletions src/bottom-bar/accept-button/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { AcceptButton } from './accept-button.js'
9 changes: 9 additions & 0 deletions src/bottom-bar/accept-button/use-accept-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useDataMutation } from '@dhis2/app-runtime'

export const ACCEPT_DATA_MUTATION = {
resource: 'dataAcceptances',
type: 'create',
params: ({ wf, pe, ou }) => ({ wf, pe, ou }),
}

export const useAcceptData = () => useDataMutation(ACCEPT_DATA_MUTATION)
65 changes: 65 additions & 0 deletions src/bottom-bar/approve-button/approve-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useAlert } from '@dhis2/app-runtime'
import i18n from '@dhis2/d2-i18n'
import { Button } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React, { useState } from 'react'
import { useWorkflowContext } from '../../workflow-context/index.js'
import { ApproveModal } from './approve-modal/index.js'
import { useApproveData } from './use-approve-data.js'

const ApproveButton = ({ disabled }) => {
// state
const [unexpectedError, setUnexpectedError] = useState(null)
const [showApproveModal, setShowApproveModal] = useState(false)
const { params, refresh } = useWorkflowContext()

// callbacks
const showApprovalDialog = () => setShowApproveModal(true)
const hideApprovalDialog = () => setShowApproveModal(false)
const onApprove = () => {
const { wf, pe, ou } = params
approveData({ wf, pe, ou })
}

// api
const { show: showApprovalSuccess } = useAlert(i18n.t('Approval saved'))
const [approveData, { loading, error: approveError }] = useApproveData({
onComplete: () => {
showApprovalSuccess()
refresh()
hideApprovalDialog()
},
onError: e => setUnexpectedError(e),
})

// derived state
const error = approveError || unexpectedError

return (
<>
<Button
primary
small
disabled={disabled}
onClick={showApprovalDialog}
>
{i18n.t('Approve')}
</Button>

{showApproveModal && (
<ApproveModal
error={error}
loading={loading}
onApprove={onApprove}
onCancel={hideApprovalDialog}
/>
)}
</>
)
}

ApproveButton.propTypes = {
disabled: PropTypes.bool,
}

export { ApproveButton }
110 changes: 110 additions & 0 deletions src/bottom-bar/approve-button/approve-modal/approve-modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import i18n from '@dhis2/d2-i18n'
import {
Button,
ButtonStrip,
Modal,
ModalTitle,
ModalContent,
ModalActions,
NoticeBox,
} from '@dhis2/ui'
import PropTypes from 'prop-types'
import React from 'react'
import { useWorkflowContext } from '../../../workflow-context/index.js'
import styles from './approve-modal.module.css'

// Hendrik is working on some changes that will allow to get the current
// period value from the url, until we can leverage on that,
// we don't display the period
// @TODO: Implement period display once possible
const TODO_GET_PERIOD = false

const ApproveModal = ({ onApprove, onCancel, error }) => {
const { dataSets } = useWorkflowContext()
const count = dataSets.length

return (
<Modal>
<ModalTitle>
{count > 1
? i18n.t('Approving {{count}} data sets', { count })
: i18n.t('Approving {{count}} data set', { count })}
</ModalTitle>

<ModalContent>
<div className={styles.summary}>
{!TODO_GET_PERIOD && (
<h1 className={styles.summaryTitle}>
{count > 1
? i18n.t(
'{{count}} data sets will be approved:',
{ count }
)
: i18n.t(
'{{count}} data set will be approved:',
{ count }
)}
</h1>
)}

{TODO_GET_PERIOD && (
<h1 className={styles.summaryTitle}>
{count > 1
? i18n.t(
'{{count}} data sets for {{period}} will be approved:',
{ count }
)
: i18n.t(
'{{count}} data set for {{period}} will be approved:',
{ count }
)}
</h1>
)}

<ul className={styles.summaryList}>
{dataSets.map(({ id, displayName }) => (
<li className={styles.summaryListItem} key={id}>
{displayName}
</li>
))}
</ul>
</div>

<p className={styles.confirmationStatement}>
{i18n.t('Are you sure you want to approve this data?')}
</p>

{error && (
<NoticeBox
error
title={i18n.t(
'There was a problem saving this approval'
)}
>
{i18n.t(
'This data couldn’t be approved. Try again, or contact your system administrator.'
)}
</NoticeBox>
)}
</ModalContent>

<ModalActions>
<ButtonStrip>
<Button onClick={onCancel}>{i18n.t('Cancel')}</Button>

<Button primary onClick={onApprove}>
{i18n.t('Approve')}
</Button>
</ButtonStrip>
</ModalActions>
</Modal>
)
}

ApproveModal.propTypes = {
onApprove: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
error: PropTypes.instanceOf(Error),
}

export { ApproveModal }
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.summary {
background: var(--colors-grey100);
padding: var(--spacers-dp16);
margin: 0 0 var(--spacers-dp16);
}

.summaryTitle {
margin: 0 0 var(--spacers-dp16);
font-size: inherit;
font-weight: inherit;
}

.summaryList {
margin: 0;
}

.confirmationStatement {
margin: 0;
}
1 change: 1 addition & 0 deletions src/bottom-bar/approve-button/approve-modal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ApproveModal } from './approve-modal.js'
1 change: 1 addition & 0 deletions src/bottom-bar/approve-button/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ApproveButton } from './approve-button.js'
10 changes: 10 additions & 0 deletions src/bottom-bar/approve-button/use-approve-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useDataMutation } from '@dhis2/app-runtime'

export const APPROVE_DATA_MUTATION = {
resource: 'dataApprovals',
type: 'create',
params: ({ wf, pe, ou }) => ({ wf, pe, ou }),
}

export const useApproveData = ({ onComplete, onError }) =>
useDataMutation(APPROVE_DATA_MUTATION, { onComplete, onError })
13 changes: 13 additions & 0 deletions src/bottom-bar/bottom-bar-item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PropTypes from 'prop-types'
import React from 'react'
import styles from './bottom-bar-item.module.css'

const BottomBarItem = ({ children }) => {
return <div className={styles.bottomBarItem}>{children}</div>
}

BottomBarItem.propTypes = {
children: PropTypes.any.isRequired,
}

export { BottomBarItem }
5 changes: 5 additions & 0 deletions src/bottom-bar/bottom-bar-item.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.bottomBarItem {
display: flex;
align-items: center;
margin-right: var(--spacers-dp8);
}
Loading

0 comments on commit 1f5a77e

Please sign in to comment.