Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ This is the repo that you will use during the Web Academy exercises. Please fork

Before participating, make sure you have gone through [the pre-requisites](https://dhis2.github.io/academy-web-app-dev/docs/before-academy/) for the academy.

For more information about the academy and the topics taught, check the website: https://dhis2.github.io/academy-web-app-dev/
For more information about the academy and the topics taught, check the website: https://dhis2.github.io/academy-web-app-dev/
27 changes: 27 additions & 0 deletions i18n/en.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
msgid ""
msgstr ""
"Project-Id-Version: i18next-conv\n"
"MIME-Version: 1.0\n"
"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-04-17T15:22:55.841Z\n"
"PO-Revision-Date: 2024-04-17T15:22:55.841Z\n"

msgid "Add an attribute"
msgstr "Add an attribute"

msgid "Attribute name"
msgstr "Attribute name"

msgid "Value Type"
msgstr "Value Type"

msgid "Text"
msgstr "Text"

msgid "Number"
msgstr "Number"

msgid "Save"
msgstr "Save"
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
},
"dependencies": {
"@dhis2/app-runtime": "^3.10.3",
"@dhis2/ui": "^9.4.4",
"react-router-dom": "^6.22.3"
}
}
12 changes: 12 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { HashRouter, Navigate, Route, Routes } from 'react-router-dom'
import styles from './App.module.css'
import { Navigation } from './navigation/index.js'
import { Form, Home, Attributes } from './views/index.js'
import { AttributeForm } from './views/AttributeForm.js'
import AttributeCreateForm from './views/CreateAttributeForm.js'


const MyApp = () => (
<HashRouter
Expand Down Expand Up @@ -42,6 +45,15 @@ const MyApp = () => (
path="/attributes"
element={<Attributes />}
/>

<Route
// Attributes route, will render "Attributes" component
// when "/attributes" is the current url
exact
path="/addattributes"
element={<AttributeCreateForm />}
/>

<Route
// functions as default route, redirects to home
// when invalid path is provided
Expand Down
15 changes: 12 additions & 3 deletions src/navigation/Navigation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import PropTypes from 'prop-types'
import React from 'react'
import {Menu} from '@dhis2/ui'
import {MenuItem} from '@dhis2/ui'

// @TODO: Import the `Menu` and `MenuItem` components
import { useNavigate, useMatch } from 'react-router-dom'

Expand All @@ -17,7 +20,7 @@ const NavigationItem = ({ path, label }) => {
const onClick = () => navigate(path)

// @TODO: Use the `MenuItem` component instead of the `div`
return <div>{label}</div>
return <Menu><MenuItem active={isActive} label={label} onClick={onClick}/></Menu>
}

NavigationItem.propTypes = {
Expand All @@ -27,7 +30,7 @@ NavigationItem.propTypes = {

export const Navigation = () => (
// @TODO: Use the `Menu` components instead of the `div`
<div>
<Menu>
<NavigationItem
// Menu item for the home page
label="Home"
Expand All @@ -40,10 +43,16 @@ export const Navigation = () => (
path="/attributes"
/>

<NavigationItem
// Menu item for the meta data page
label="Create Attributes"
path="/addattributes"
/>

<NavigationItem
// Menu item for the Form page
label="Form"
path="/form"
/>
</div>
</Menu>
)
101 changes: 101 additions & 0 deletions src/views/AttributeForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
ReactFinalForm,
SingleSelectFieldFF,
InputFieldFF,
SwitchFieldFF,
Button,
AlertBar,
Center,
} from '@dhis2/ui'
import React from 'react'
import { useAlert } from '@dhis2/app-runtime'
import styles from './Form.module.css'
import i18n from '@dhis2/d2-i18n'

/**
* This is just a function to demonstrate the values when the form is submitted
* It takes the form's values (which is an object), transforms it into readable JSON,
* and then finally displays the values in an alert box
*
* @param {Object} values
* @param {string} values.title
* @param {string} values.surname
* @param {string} values.firstname
* @param {string} values.email
* @param {string} values.email-confirmation
* @param {bool} values.newsletter
*/

const { Field, Form: RFForm } = ReactFinalForm

export const AttributeForm = () => {
const { show } = useAlert(
(values) => {
return JSON.stringify(values,null,2)
},

(values) => {
if (values.title === '1. anaconda'){
return { success: true }
}
if (values.title === '2. bobouin'){
return { warning: true }
}
if (values.title === '3. mangouste'){
return { critical: true }
}

}
)

const alertValues = (values) => {
show(values)
}

return (
<div>
<h1>{i18n.t('Add an attribute')}</h1>

<RFForm onSubmit={alertValues}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<div className={styles.row}>
<Field
required
name="name"
label={i18n.t('Attribute name')}
component={InputFieldFF}
validate={''}
/>
</div>
<div className={styles.row}>
<Field
name="valueType"
label={i18n.t('Value Type')}
component={SingleSelectFieldFF}
className={styles.title}
initialValue="TEXT"
options={[
{
label: i18n.t('Text'),
value: 'TEXT',
},
{
label: i18n.t('Number'),
value: 'NUMBER',
},
]}
/>
</div>
<div className={styles.row}>
<Button type="submit" primary>
{i18n.t('Save')}
</Button>
</div>
</form>
)}
</RFForm>
</div>
)

}
73 changes: 66 additions & 7 deletions src/views/Attributes.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,81 @@
import React from 'react'
import {
CenteredContent,
CircularLoader,
NoticeBox,
Table,
TableBody,
TableCell,
TableCellHead,
TableHead,
TableRow,
TableRowHead,
} from '@dhis2/ui'
import { useGetAttributes } from '../hooks/index.js'
import { useDataQuery } from '@dhis2/app-runtime'

const query = {
attributesList: {
resource: 'attributes',
params: {
fields: ['displayName', 'id', 'unique', 'created'],
order: 'created.desc',
},
},
}

export const Attributes = () => {
// we get the data using a custom hook
// we will update this implementation after learning about app-runtime
const { loading, error, data } = useGetAttributes()
const { loading, error, data } = useDataQuery(query)

if (loading) {
return (
<CenteredContent>
<CircularLoader></CircularLoader>
</CenteredContent>
)
}

if (error) {
return (
<NoticeBox error>
{error.message}
</NoticeBox>
)
}


return (
<div>
<h1>Attributes</h1>
<p>loading: {JSON.stringify(loading)}</p>
<p>error message: {error?.message}</p>
{
// if there is any data available
data?.attributes?.attributes && (
<pre>
{JSON.stringify(data.attributes.attributes, null, 4)}
</pre>
data?.attributesList?.attributes && (
<Table>
<TableHead>
<TableRowHead>
<TableCellHead>id</TableCellHead>
<TableCellHead>displayName</TableCellHead>
<TableCellHead>unique</TableCellHead>
<TableCellHead>Created Date</TableCellHead>
</TableRowHead>
</TableHead>
<TableBody>
{data.attributesList.attributes.map(
({ id, displayName, unique, created }) => (
<TableRow key={id}>
<TableCell>{id}</TableCell>
<TableCell>{displayName}</TableCell>
<TableCell>
{unique ? 'Yes' : 'No'}
</TableCell>
<TableCell>{created}</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
)
}
</div>
Expand Down
81 changes: 81 additions & 0 deletions src/views/CreateAttributeForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import i18n from '@dhis2/d2-i18n'
import {
Button,
InputFieldFF,
hasValue,
ReactFinalForm,
SingleSelectFieldFF,
} from '@dhis2/ui'
import React from 'react'
import styles from './Form.module.css'
import { useDataMutation } from '@dhis2/app-runtime'

const { Field, Form: RFForm } = ReactFinalForm

const mutation = {
resource:"attributes",
type:"create",
data: ({name,type}) => {
return {
name,
type,
}
}
}
const AttributeCreateForm = () => {

const [mutate] = useDataMutation(mutation)

const onSubmit = async (values) => {
// @todo: add the mutation
mutate(values)
}

return (
<div>
<h1>{i18n.t('Add an attribute')}</h1>

<RFForm onSubmit={onSubmit}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<div className={styles.row}>
<Field
required
name="name"
label={i18n.t('Attribute name')}
component={InputFieldFF}
validate={hasValue}
/>
</div>
<div className={styles.row}>
<Field
name="valueType"
label={i18n.t('Value Type')}
component={SingleSelectFieldFF}
className={styles.title}
initialValue="TEXT"
options={[
{
label: i18n.t('Text'),
value: 'TEXT',
},
{
label: i18n.t('Number'),
value: 'NUMBER',
},
]}
/>
</div>
<div className={styles.row}>
<Button type="submit" primary>
{i18n.t('Save')}
</Button>
</div>
</form>
)}
</RFForm>
</div>
)
}

export default AttributeCreateForm
Loading