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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ 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/

Edited by Matthew Edor
52 changes: 52 additions & 0 deletions i18n/en.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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-17T09:33:24.538Z\n"
"PO-Revision-Date: 2024-04-17T09:33:24.538Z\n"

msgid "Home"
msgstr "Maison"
msgid "API_VERSION"
msgstr "Exécution sur la version API {{apiVersion}}"
msgid "ATTR_INFO"
msgstr "Attributs visibles par {{name}} ({{email}})"
msgid "YES"
msgstr "Oui"

msgid "NO"
msgstr "Non"

msgid "ADD_ATTRIBUTE"
msgstr "Ajouter un attribut"

msgid "ATTR_TYPE"
msgstr "Type de valeur"

msgid "ATTR_NAME"
msgstr "Nom d'attribut"

msgid "ATTR_NAME_SHORT"
msgstr "Nom"

msgid "ATTR_UNIQUE"
msgstr "Unique"

msgid "ATTR_TYPE_TEXT"
msgstr "Texte"

msgid "ATTR_TYPE_NUMBER"
msgstr "Nombre"

msgid "ATTR_CREATED"
msgstr "Un attribut avec l'ID {{ID}} a été créé"

msgid "SAVE"
msgstr "Sauvegarder"

msgid "ATTR_ACTIONS"
msgstr "Actions"

52 changes: 52 additions & 0 deletions i18n/fr.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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-17T09:33:24.538Z\n"
"PO-Revision-Date: 2024-04-17T09:33:24.538Z\n"

msgid "Home"
msgstr "Maison"
msgid "API_VERSION"
msgstr "Exécution sur la version API {{apiVersion}}"
msgid "ATTR_INFO"
msgstr "Attributs visibles par {{name}} ({{email}})"
msgid "YES"
msgstr "Oui"

msgid "NO"
msgstr "Non"

msgid "ADD_ATTRIBUTE"
msgstr "Ajouter un attribut"

msgid "ATTR_TYPE"
msgstr "Type de valeur"

msgid "ATTR_NAME"
msgstr "Nom d'attribut"

msgid "ATTR_NAME_SHORT"
msgstr "Nom"

msgid "ATTR_UNIQUE"
msgstr "Unique"

msgid "ATTR_TYPE_TEXT"
msgstr "Texte"

msgid "ATTR_TYPE_NUMBER"
msgstr "Nombre"

msgid "ATTR_CREATED"
msgstr "Un attribut avec l'ID {{ID}} a été créé"

msgid "SAVE"
msgstr "Sauvegarder"

msgid "ATTR_ACTIONS"
msgstr "Actions"

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"private": true,
"scripts": {
"build": "d2-app-scripts build",
"start": "d2-app-scripts start",
"start": "d2-app-scripts start --proxy https://dev.im.dhis2.org/academy-web --proxyPort 8082",
"test": "d2-app-scripts test",
"deploy": "d2-app-scripts deploy",
"lint": "d2-style check",
Expand Down
1 change: 1 addition & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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 './locales/index.js';

const MyApp = () => (
<HashRouter
Expand Down
12 changes: 5 additions & 7 deletions src/navigation/Navigation.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Menu, MenuItem } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React from 'react'
// @TODO: Import the `Menu` and `MenuItem` components
import { useNavigate, useMatch } from 'react-router-dom'
import { useMatch, useNavigate } from 'react-router-dom'

const NavigationItem = ({ path, label }) => {
// function to navigate to different route
Expand All @@ -16,8 +16,7 @@ const NavigationItem = ({ path, label }) => {
// eslint-disable-next-line no-unused-vars
const onClick = () => navigate(path)

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

NavigationItem.propTypes = {
Expand All @@ -26,8 +25,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 @@ -45,5 +43,5 @@ export const Navigation = () => (
label="Form"
path="/form"
/>
</div>
</Menu>
)
189 changes: 176 additions & 13 deletions src/views/Attributes.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,187 @@
import { useAlert, useDataMutation, useDataQuery } from '@dhis2/app-runtime'
import i18n from '@dhis2/d2-i18n'
import {
Button,
CenteredContent,
CircularLoader,
DataTable,
DataTableCell,
DataTableColumnHeader,
DataTableRow,
hasValue,
InputFieldFF,
NoticeBox,
ReactFinalForm,
SingleSelectFieldFF,
TableBody,
TableHead,
Pagination
} from '@dhis2/ui'
import React from 'react'
import { useGetAttributes } from '../hooks/index.js'
import styles from './Form.module.css'

const query = {
attributes: {
resource: 'attributes',
params: {
order: 'created:desc',
pageSize: 5,
fields: ['displayName', 'unique', 'id', 'valueType']
},
},
users: {
resource: 'me',
params: {
fields: ['name', 'email']
}
}
}

const mutation = {
resource: 'attributes',
type: 'create',
data: ({value}) => value
}

const deleteMutation = {
resource: 'attributes',
type: 'delete',
id: ({id}) => id
}

const {Field, Form: RFForm} = ReactFinalForm

export const Attributes = () => {

const [mutate, {loading: saving, error: mutateError, data: response}] = useDataMutation(mutation)
const [delMutation, {loading: deleteLoading}] = useDataMutation(deleteMutation, {
onComplete: () => refetch()
})

const {show} = useAlert(
({message}) => message,
({type = 'info'}) =>
type === 'error' ? {critical: true} : {success: true}
)

const deleteAttribute = (id) => {
delMutation({id})
}

const saveAttribute = (value) => {
console.log('Response', mutateError, response)
mutate({value})
let type = 'info';
let message = i18n.t('ATTR_CREATED', {ID: response?.uid})
if (mutateError) {
type = 'error';
message = mutateError
}

show({message, type})
refetch()
}
// 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, refetch} = useDataQuery(query)
if (loading) {
return <CenteredContent>
<CircularLoader/>
</CenteredContent>
}
if (error) {
return <NoticeBox error title="Error loading data">{error}</NoticeBox>
}
const user = data?.users;
const name = user?.name;
const email = user?.email;
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>
)
}
<p>{i18n.t('ATTR_INFO', {name, email})}</p>
<DataTable>
<TableHead>
<DataTableColumnHeader>
{i18n.t('ATTR_NAME_SHORT')}
</DataTableColumnHeader>
<DataTableColumnHeader>
{i18n.t('ATTR_UNIQUE')}
</DataTableColumnHeader>
<DataTableColumnHeader>
{i18n.t('ATTR_TYPE')}
</DataTableColumnHeader>
<DataTableColumnHeader>
{i18n.t('ATTR_ACTIONS')}
</DataTableColumnHeader>
</TableHead>
{data?.attributes?.attributes && (
<TableBody>
{data.attributes.attributes.map((attribute) => {
const {id, displayName, unique, valueType} = attribute;
return <DataTableRow key={id}>
<DataTableCell>{displayName}</DataTableCell>
<DataTableCell>{unique ? i18n.t('YES') : i18n.t('NO')}</DataTableCell>
<DataTableCell>{valueType}</DataTableCell>
<DataTableCell>
<Button small destructive disabled={deleteLoading}
onClick={() => deleteAttribute(id)}>Delete</Button>
</DataTableCell>
</DataTableRow>
})}
</TableBody>
)}
</DataTable>
<br/>
<hr/>
<p>{i18n.t('ADD_ATTRIBUTE')}</p>
{saving && (
<CenteredContent>
<CircularLoader/>
</CenteredContent>
)}
<RFForm onSubmit={saveAttribute}>
{({handleSubmit}) => (
<form onSubmit={handleSubmit}>
<div className={styles.row}>
<Field
name="name"
label={i18n.t('ATTR_NAME')}
required
component={InputFieldFF}
type="text"
placeholder={i18n.t('ATTR_NAME')}
className={styles.title}
validate={hasValue}
/>
</div>
<div className={styles.row}>
<ReactFinalForm.Field
name="valueType"
label={i18n.t('ATTR_TYPE')}
component={SingleSelectFieldFF}
className={styles.title}
required
options={[
{label: 'TEXT', value: 'TEXT'},
{label: 'NUMBER', value: 'NUMBER'}
]}
validate={hasValue}
/>
</div>
<div className={styles.row}>
<Button
ariaLabel="Button"
type="submit"
primary
disabled={saving}
value="default"
>
{i18n.t('SAVE')}
</Button>
</div>
</form>
)}
</RFForm>
</div>
)
}
Loading