Skip to content

Commit

Permalink
docs: apply recommendations
Browse files Browse the repository at this point in the history
  • Loading branch information
Topener committed Jan 14, 2025
1 parent e452ae4 commit 1c33664
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 41 deletions.
8 changes: 2 additions & 6 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@ The DHIS2 application runtime provides a common, consistent, single-dependency r

## Installation

```bash
yarn add @dhis2/app-runtime
```

or
To use App Runtime in your project, install it as a dependency using `yarn`:

```bash
npm install @dhis2/app-runtime
yarn add @dhis2/app-runtime
```

:::info
Expand Down
Binary file added docs/guides/assets/alert-states.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 50 additions & 19 deletions docs/guides/display-alerts.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,43 @@ Now that you have imported the `useAlert` hook, you can use it to send a default
The `show` function is used to display the alert, while the `hide` function is used to hide the alert. After hiding (automatically or by code), the alert will be removed from the alert stack. But you can re-add it by calling the `show` function again.

```js
const { show, hide } = useAlert('This is a default alert message')

// Display the alert, somewhere else in your code
show()
const App() => {
// highlight-start
const { show, hide } = useAlert('This is a default alert message')
// highlight-end

// Display the alert, somewhere later in your code
// highlight-start
show()
// highlight-end
}
```

To hide the alert, you can call the `hide` function, anywhere in your code where you have access to the `hide` function.

```js
hide()
const App() => {
// [...]
// highlight-start
hide()
// highlight-end
}
```

## Step 3: Adding options to the alert

You can also add options to the alert, such as the duration of the alert, the type of alert, and the action to be taken when the alert is clicked.

```js

const { show, hide } = useAlert('This is a default alert message', {
duration: 30000, // 30 seconds
critical: true, // show the alert as critical
permanent: true // don't auto-hide the alert
})
const App() => {
// highlight-start
const { show, hide } = useAlert('This is a default alert message', {
duration: 30000, // 30 seconds
critical: true, // show the alert as critical
permanent: true // don't auto-hide the alert
})
// highlight-end
}
```

:::info All Options
Expand All @@ -66,22 +80,39 @@ You can see the full list of options in the [AlertBar component documentation](/

You can also display dynamic alerts by passing a function to the `useAlert` hook. This function will receive the arguments passed to the `show` function.

```js
The code below will be capable of displaying alerts with different messages and options depending on the arguments passed to the `show` function. Two of the states represented, in the image below:

const { show, hide } = useAlert(
({ program }) => `Successfully updated ${program}`,
({ isActive }) => isActive ? { critical: true } : { success: true }
)
![Alert States](assets/alert-states.png)

// Display the alert, somewhere else in your code
The first state is a critical alert with the message `Failed to update tb`, while the second state is a success alert with the message `Successfully updated tb`.

show({ program: 'tb', isActive: true })
```js
const App() => {
// highlight-start
const { show, hide } = useAlert(
({ program, success }) => (success ? `Successfully updated ${program}` : `Failed to update ${program}`),
({ success }) => isActive ? { critical: true } : { success: true }
)
// highlight-end

// Display the alert, somewhere else in your code
// highlight-start
show({ program: 'tb', success: true })
// highlight-end
}
```

In the example above, the alert will be displayed with the message `Successfully updated tb` and will be shown as critical. However, if you were to call the `show` function with `isActive: false`, the alert would be shown as a success alert. Changing the program name will also change the message displayed in the alert dynamically. Allowing you to only specify this alert once and reuse it with different messages and options depending on which program is updated.
In the example above, the alert message will be displayed as `Failed to update tb` and will be shown as critical. However, if you were to call the `show` function with `success: true`, the alert would show with a success state and the message `Successfully updated tb`. Updating the name will also change the message displayed in the alert dynamically.

This allows you to configure the Alert once and reuse it with different messages and options depending on the state of your application.

## Conclusion

In this guide, you learned how to display alerts in your application using the `useAlert` hook from the `@dhis2/app-runtime` library. You also learned how to handle different alert states and display dynamic alerts. You can now use this knowledge to display alerts in your application and provide feedback to your users.

You can read more about the `useAlert` hook in the [official documentation](/docs/app-runtime/hooks/useAlert), and find all properties to be used in the `AlertBar` component in the [AlertBar documentation](/docs/ui/components/alertbar#props).

## Additional Resources

- [AlertBar Component Documentation](/docs/ui/components/alertbar)
- [AlertBar Demo](pathname:///demo/?path=/story/alert-bar--states)
68 changes: 52 additions & 16 deletions docs/guides/using-dataquery.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: How to use useDataQuery in your App
sidebar_label: How to use useDataQuery
---

To fetch data in your DHIS2 Web Application, you can use the `useDataQuery` hook from the `@dhis2/app-runtime` library. This hook is a custom hook that simplifies the process of fetching data from the DHIS2 API. There's no need to do the API calls manually, as the `useDataQuery` hook handles this for you, including authentication.
To fetch data in your DHIS2 Web Application, you can use the `useDataQuery` hook from the `@dhis2/app-runtime` library. This React hook simplifies the process of fetching data from the DHIS2 API. There's no need to make API calls manually using `fetch` because the `useDataQuery` hook handles this for you, including authentication.

In this guide, we'll walk you through, step by step, how to use the `useDataQuery` hook in your application to fetch data from the DHIS2 API. We'll be fetching Organisation Units and applying several filters to the query.

Expand All @@ -30,30 +30,43 @@ Now that you have imported the `useDataQuery` hook, you can use it in your compo

The next step is to define the query that you want to execute. The query is an object that contains the properties of the data you want to fetch. In this example, we'll be fetching Organisation Units from the DHIS2 API.

You should define the query outside the render loop, such as on top of your file. This is because the query object is a static object that doesn't change during the lifecycle of the component.

```js
// highlight-start
const query = {
orgUnits: {
resource: 'organisationUnits',
params: {
fields: ['id', 'displayName', 'level'],
paging: false
paging: true
}
}
};
// highlight-end

const App() => {

}
```

In the query object above, we define a property called `orgUnits` that contains the details of the query. The `resource` property specifies the endpoint of the API that we want to fetch data from. The `params` property contains additional parameters that we want to apply to the query, such as the fields we want to fetch and whether we want to paginate the results.

In this case we're not paginating the results, so we set `paging` to `false`. However, in any production application, you should consider paginating the results to avoid fetching a large amount of data at once if you don't need it.

We'll be explain pagination at a later step in this guide.
We've enabled pagination by setting the `paging` property to `true`. This means that the results will be paginated, and we'll only get a subset of the results. This is the recommended approach as it is lighter on both the server and uses less data. We'll explain pagination at a later step in this guide.

## Step 3: Execute the query

Now that we have defined the query, we can execute it using the `useDataQuery` hook. The `useDataQuery` hook takes the query object as an argument and returns an object containing the data, error, and loading state of the query.

```jsx
const { loading, error, data } = useDataQuery(query);

const query = {}; // defined query object as explained in step 2

const App() => {
// highlight-start
const { loading, error, data } = useDataQuery(query);
// highlight-end
}
```

In the code above, we use object destructuring to extract the `loading`, `error`, and `data` properties from the object returned by the `useDataQuery` hook. The `loading` property indicates whether the query is still loading, the `error` property contains any error that occurred during the query, and the `data` property contains the data returned by the query.
Expand All @@ -63,6 +76,11 @@ In the code above, we use object destructuring to extract the `loading`, `error`
Now that we have fetched the data, we can display the data returned by the query in our component. We can use the `loading` and `error` properties to conditionally render the data based on the state of the query.

```jsx
// use the CircularLoader component from the @dhis2/ui library
import { CircularLoader } from '@dhis2/ui';

const App() => {
// highlight-start
// show an error when the query fails
if (error) {
return <span>ERROR: {error.message}</span>;
Expand All @@ -73,31 +91,45 @@ Now that we have fetched the data, we can display the data returned by the query
return <CircularLoader />;
}

if (!data?.orgUnits?.organisationUnits?.length) {
return <span>No organisation units found</span>
}

// show the data when the query is successful
return (
<div>
<>
<h1>Organisation Units</h1>
<ul>
{data.orgUnits.organisationUnits.map(orgUnit => (
<li key={orgUnit.id}>{orgUnit.displayName}</li>
))}
</ul>
</div>
</>
);
// highlight-end
}
```
In the code above, we first check if there is an error by checking the `error` property. If there is an error, we display the error message. Next, we check if the query is still loading by checking the `loading` property. If the query is still loading, we display a loading spinner. Finally, if the query is successful, we display the data returned by the query.
Keep in mind that the structure of the data returned by the query may vary depending on the query you execute. In this example, we are fetching Organisation Units, so the data returned by the query contains an `organisationUnits` property that contains an array of Organisation Units. We're also assuming the data structure is always the same. This is not really defensive programming, so you should always check if the data is in the expected format.
:::note
We're expecting data to be in `data.orgUnits`, this is because the query object we defined in step 2 is called `orgUnits`.
:::
Keep in mind that the structure of the data returned by the query may vary depending on the query you execute. In this example, we are fetching Organisation Units, so the data returned by the query contains an `organisationUnits` property that contains an array of Organisation Units. We're also assuming the data structure is always the same. This is not really defensive programming, so you should always check if the data is in the expected format.
To make the code more robust, you should check if the data exists in the structure you expect. You could, for example, use optional chaining for this.
For this reason we've added this check in the code above:
```jsx
{data?.orgUnits?.organisationUnits?.map(orgUnit => (
<li key={orgUnit.id}>{orgUnit.displayName}</li>
))}
if (!data?.orgUnits?.organisationUnits?.length) {
```
This code checks if the `orgUnits` property exists in the `data` object, if the `organisationUnits` property exists in the `orgUnits` object, and if the `organisationUnits` array has a length greater than 0. If any of these conditions are not met, we display a message indicating that no Organisation Units were found.
:::info Defensive programming
Defensive programming is a practice where you write code that anticipates and handles errors. This can help prevent your application from crashing when unexpected errors occur. Always check if the data is in the expected format before using it in your application, even if you trust the source the data is coming from.
:::
## Step 5: Apply pagination
In the query object, you can specify the `paging` property to paginate the results. By default, the `paging` property is set to `true`, which means that the results will be paginated. You can specify the `pageSize` property to control the number of items per page.
Expand Down Expand Up @@ -139,9 +171,13 @@ In the code above, we set the `page` property to `2`, which means that we want t
const pageCount = data.orgUnits.pager.pageCount;
```
:::note `pageCount` not always available
The `pageCount` property is not always available in the response. You should check if the property exists before using it in your application.
:::
## Step 6: Apply filters
You can apply filters to the query to filter the results based on certain criteria. You can use the `filter` property to specify the filters you want to apply. In this example we'll be filtering the Organisation Units where the name contains the word "Health".
You can apply filters to the query to filter the results based on certain criteria. You can use the `filter` property to specify the filters you want to apply. In this example we'll be filtering the Organisation Units where the name contains the word "health".
```js

Expand All @@ -156,7 +192,7 @@ const query = {
};
```
Executing the query above will return all Organisation Units where the name contains the word "Health". You can use the `filter` property to apply more complex filters by supplying an array of filter criteria. In this example, we'll be filtering the Organisation Units where the name contains the word "Health" and the level is 4.
Executing the query above will return all Organisation Units where the name contains the word "health". You can use the `filter` property to apply more complex filters by supplying an array of filter criteria. In this example, we'll be filtering the Organisation Units where the name contains the word "health" and the level is 4.
```js

Expand All @@ -165,7 +201,7 @@ const query = {
resource: 'organisationUnits',
params: {
fields: ['id', 'displayName', 'level'],
filter: ['displayName:like:health','level:eq:4'],
filter: ['displayName:ilike:health','level:eq:4'],
}
}
};
Expand Down

0 comments on commit 1c33664

Please sign in to comment.