@dhis2/create-app 5.3.0-alpha.1 → 5.3.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -2
- package/src/index.js +46 -37
- package/src/utils/getPackageManager.js +15 -0
- package/templates/template-ts-dataelements/.prettierrc.mjs +10 -0
- package/templates/template-ts-dataelements/README.md +45 -0
- package/templates/template-ts-dataelements/d2.config.js +12 -0
- package/templates/template-ts-dataelements/eslint.config.mjs +13 -0
- package/templates/template-ts-dataelements/i18n/en.pot +42 -0
- package/templates/template-ts-dataelements/package.json +35 -0
- package/templates/template-ts-dataelements/pnpm-lock.yaml +12950 -0
- package/templates/template-ts-dataelements/pnpm-workspace.yaml +17 -0
- package/templates/template-ts-dataelements/src/App.module.css +22 -0
- package/templates/template-ts-dataelements/src/App.test.tsx +21 -0
- package/templates/template-ts-dataelements/src/App.tsx +95 -0
- package/templates/template-ts-dataelements/src/components/DataElementsList.module.css +9 -0
- package/templates/template-ts-dataelements/src/components/DataElementsList.tsx +132 -0
- package/templates/template-ts-dataelements/tsconfig.json +17 -0
- package/templates/template-ts-dataelements/types/global.d.ts +8 -0
- package/templates/template-ts-dataelements/types/modules.d.ts +4 -0
- package/templates/template-ts-dataelements/viteConfigExtensions.mts +15 -0
- package/templates/template-ts-dataelements-react-router/.prettierrc.mjs +10 -0
- package/templates/template-ts-dataelements-react-router/README.md +45 -0
- package/templates/template-ts-dataelements-react-router/d2.config.js +12 -0
- package/templates/template-ts-dataelements-react-router/eslint.config.mjs +13 -0
- package/templates/template-ts-dataelements-react-router/i18n/en.pot +39 -0
- package/templates/template-ts-dataelements-react-router/package.json +36 -0
- package/templates/template-ts-dataelements-react-router/pnpm-lock.yaml +12982 -0
- package/templates/template-ts-dataelements-react-router/pnpm-workspace.yaml +17 -0
- package/templates/template-ts-dataelements-react-router/src/App.module.css +61 -0
- package/templates/template-ts-dataelements-react-router/src/App.test.tsx +21 -0
- package/templates/template-ts-dataelements-react-router/src/App.tsx +94 -0
- package/templates/template-ts-dataelements-react-router/src/AppWrapper.tsx +31 -0
- package/templates/template-ts-dataelements-react-router/src/components/About.tsx +15 -0
- package/templates/template-ts-dataelements-react-router/src/components/AppMenu.tsx +31 -0
- package/templates/template-ts-dataelements-react-router/src/components/DataElementsList.module.css +9 -0
- package/templates/template-ts-dataelements-react-router/src/components/DataElementsList.tsx +132 -0
- package/templates/template-ts-dataelements-react-router/tsconfig.json +17 -0
- package/templates/template-ts-dataelements-react-router/types/global.d.ts +8 -0
- package/templates/template-ts-dataelements-react-router/types/modules.d.ts +4 -0
- package/templates/template-ts-dataelements-react-router/viteConfigExtensions.mts +15 -0
- package/templates/template-ts-dataelements-react-router.zip +0 -0
- package/templates/template-ts-dataelements.zip +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# These hoists are needed for @dhis2/app-shell since it uses directly some libraries that were hoisted by yarn 1
|
|
2
|
+
# This is a better alternative than pnpm's shamefullyHoist https://pnpm.io/settings#shamefullyhoist until we update the way that @dhis2/app-shell works
|
|
3
|
+
publicHoistPattern:
|
|
4
|
+
- '@dhis2/ui'
|
|
5
|
+
- 'typeface-roboto'
|
|
6
|
+
- '@dhis2/app-adapter'
|
|
7
|
+
- 'prop-types'
|
|
8
|
+
- '@dhis2/d2-i18n'
|
|
9
|
+
- 'post-robot'
|
|
10
|
+
- 'styled-jsx'
|
|
11
|
+
- '@dhis2/app-adapter'
|
|
12
|
+
- '@dhis2/pwa'
|
|
13
|
+
|
|
14
|
+
onlyBuiltDependencies:
|
|
15
|
+
- '@dhis2/cli-helpers-engine'
|
|
16
|
+
- core-js-pure
|
|
17
|
+
- esbuild
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
.appWrapper {
|
|
2
|
+
display: grid;
|
|
3
|
+
grid-template-areas: 'sidebar' 'main' 'footer';
|
|
4
|
+
font-size: 14px;
|
|
5
|
+
grid-template-columns: auto;
|
|
6
|
+
grid-template-rows: auto 1fr auto;
|
|
7
|
+
overflow: auto;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.sidebar {
|
|
11
|
+
grid-area: sidebar;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.footer {
|
|
15
|
+
grid-area: footer;
|
|
16
|
+
width: 100%;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.main {
|
|
20
|
+
grid-area: main;
|
|
21
|
+
width: 100%;
|
|
22
|
+
padding-inline-start: 10px;
|
|
23
|
+
/*height: 100%;
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
font-size: 1rem;
|
|
27
|
+
max-width: 1020px;
|
|
28
|
+
margin: 0px auto;
|
|
29
|
+
padding-top: 8px; */
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.noticeContainer {
|
|
33
|
+
/* width: 768px; */
|
|
34
|
+
max-width: 340px;
|
|
35
|
+
|
|
36
|
+
margin: 16px 0;
|
|
37
|
+
align-self: center;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@media (min-width: 768px) {
|
|
41
|
+
.appWrapper {
|
|
42
|
+
display: grid;
|
|
43
|
+
grid-template-areas: 'sidebar main' 'footer footer';
|
|
44
|
+
font-size: 14px;
|
|
45
|
+
height: 100%;
|
|
46
|
+
grid-template-columns: 250px 1fr;
|
|
47
|
+
grid-template-rows: 1fr auto;
|
|
48
|
+
overflow: auto;
|
|
49
|
+
min-height: 1000px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.noticeContainer {
|
|
53
|
+
max-width: 768px;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
:global(h2),
|
|
58
|
+
:global(h3) {
|
|
59
|
+
margin: 8px;
|
|
60
|
+
margin-inline-start: 0;
|
|
61
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CustomDataProvider } from '@dhis2/app-runtime'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { createRoot } from 'react-dom/client'
|
|
4
|
+
import App from './App'
|
|
5
|
+
|
|
6
|
+
it('renders without crashing', () => {
|
|
7
|
+
const container = document.createElement('div')
|
|
8
|
+
|
|
9
|
+
const data = {
|
|
10
|
+
resource: 'test',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const root = createRoot(container)
|
|
14
|
+
root.render(
|
|
15
|
+
<CustomDataProvider data={data}>
|
|
16
|
+
<App />
|
|
17
|
+
</CustomDataProvider>
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
root.unmount()
|
|
21
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { useDataQuery } from '@dhis2/app-runtime'
|
|
2
|
+
import i18n from '@dhis2/d2-i18n'
|
|
3
|
+
import { NoticeBox } from '@dhis2/ui'
|
|
4
|
+
import React, { FC } from 'react'
|
|
5
|
+
import classes from '@/App.module.css'
|
|
6
|
+
import DataElementsList from '@/components/DataElementsList'
|
|
7
|
+
|
|
8
|
+
interface QueryResults {
|
|
9
|
+
me: {
|
|
10
|
+
name: string
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const query = {
|
|
15
|
+
me: {
|
|
16
|
+
resource: 'me',
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const MyApp: FC = () => {
|
|
21
|
+
const { error, loading, data } = useDataQuery<QueryResults>(query)
|
|
22
|
+
|
|
23
|
+
if (error) {
|
|
24
|
+
return <span>{i18n.t('ERROR')}</span>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (loading) {
|
|
28
|
+
return <span>{i18n.t('Loading...')}</span>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
<h2>{i18n.t('Hello {{name}}', { name: data?.me?.name })}</h2>
|
|
34
|
+
<div className={classes.noticeContainer}>
|
|
35
|
+
<NoticeBox title="DHIS2 Web application">
|
|
36
|
+
This application was scaffolded using DHIS2 app platform
|
|
37
|
+
(with <code>npm create @dhis2/app</code>).
|
|
38
|
+
<br />
|
|
39
|
+
For more information, you can check the DHIS2 documentation:
|
|
40
|
+
<ul>
|
|
41
|
+
<li>
|
|
42
|
+
<a
|
|
43
|
+
target="_blank"
|
|
44
|
+
href="https://developers.dhis2.org/docs/guides"
|
|
45
|
+
rel="noreferrer"
|
|
46
|
+
>
|
|
47
|
+
Web development guides
|
|
48
|
+
</a>
|
|
49
|
+
</li>
|
|
50
|
+
<li>
|
|
51
|
+
<a
|
|
52
|
+
target="_blank"
|
|
53
|
+
href="https://docs.dhis2.org/en/develop/using-the-api/dhis-core-version-master/metadata.html"
|
|
54
|
+
rel="noreferrer"
|
|
55
|
+
>
|
|
56
|
+
Documentation for the API{' '}
|
|
57
|
+
</a>
|
|
58
|
+
</li>
|
|
59
|
+
<li>
|
|
60
|
+
<a
|
|
61
|
+
target="_blank"
|
|
62
|
+
href="https://developers.dhis2.org/docs/tutorials/app-runtime-query/"
|
|
63
|
+
rel="noreferrer"
|
|
64
|
+
>
|
|
65
|
+
AppRuntime and useDataQuery hook
|
|
66
|
+
</a>
|
|
67
|
+
</li>
|
|
68
|
+
<li>
|
|
69
|
+
<a
|
|
70
|
+
target="_blank"
|
|
71
|
+
href="https://developers.dhis2.org/docs/ui/webcomponents"
|
|
72
|
+
rel="noreferrer"
|
|
73
|
+
>
|
|
74
|
+
UI Library
|
|
75
|
+
</a>
|
|
76
|
+
</li>
|
|
77
|
+
<li>
|
|
78
|
+
<a
|
|
79
|
+
target="_blank"
|
|
80
|
+
href="https://developers.dhis2.org/demo/"
|
|
81
|
+
rel="noreferrer"
|
|
82
|
+
>
|
|
83
|
+
UI library demo
|
|
84
|
+
</a>
|
|
85
|
+
</li>
|
|
86
|
+
</ul>
|
|
87
|
+
</NoticeBox>
|
|
88
|
+
</div>
|
|
89
|
+
<DataElementsList />
|
|
90
|
+
</div>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default MyApp
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { HashRouter, Route, Routes } from 'react-router'
|
|
3
|
+
import App from '@/App'
|
|
4
|
+
import classes from '@/App.module.css'
|
|
5
|
+
import AboutPage from '@/components/About'
|
|
6
|
+
import AppMenu from '@/components/AppMenu'
|
|
7
|
+
|
|
8
|
+
const AppWrapper = () => {
|
|
9
|
+
return (
|
|
10
|
+
<div className={classes.appWrapper}>
|
|
11
|
+
{/*
|
|
12
|
+
For the client-side routing to work on a DHIS2 server, it needs to use HashRouter
|
|
13
|
+
otherwise it will interfer with the server-side routing
|
|
14
|
+
*/}
|
|
15
|
+
<HashRouter>
|
|
16
|
+
<div className={classes.sidebar}>
|
|
17
|
+
<AppMenu />
|
|
18
|
+
</div>
|
|
19
|
+
<div className={classes.main}>
|
|
20
|
+
<Routes>
|
|
21
|
+
<Route path="/" element={<App />} />
|
|
22
|
+
<Route path="/about" element={<AboutPage />} />
|
|
23
|
+
</Routes>
|
|
24
|
+
</div>
|
|
25
|
+
<div className={classes.footer}>Our App</div>
|
|
26
|
+
</HashRouter>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default AppWrapper
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
const AboutPage = () => {
|
|
4
|
+
return (
|
|
5
|
+
<div>
|
|
6
|
+
<h2>About the app</h2>
|
|
7
|
+
<div>
|
|
8
|
+
A placeholder for an About page showing under a separate
|
|
9
|
+
client-side route
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default AboutPage
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Menu, MenuItem } from '@dhis2/ui'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { useNavigate, useLocation } from 'react-router'
|
|
4
|
+
|
|
5
|
+
const AppMenu = () => {
|
|
6
|
+
const navigate = useNavigate()
|
|
7
|
+
const location = useLocation()
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div>
|
|
11
|
+
<Menu>
|
|
12
|
+
<MenuItem
|
|
13
|
+
active={location.pathname === '/'}
|
|
14
|
+
onClick={() => {
|
|
15
|
+
navigate(`/`)
|
|
16
|
+
}}
|
|
17
|
+
label="Home"
|
|
18
|
+
/>
|
|
19
|
+
<MenuItem
|
|
20
|
+
active={location.pathname == '/about'}
|
|
21
|
+
onClick={() => {
|
|
22
|
+
navigate(`/about`)
|
|
23
|
+
}}
|
|
24
|
+
label="About"
|
|
25
|
+
/>
|
|
26
|
+
</Menu>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default AppMenu
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { useDataQuery } from '@dhis2/app-runtime'
|
|
2
|
+
import i18n from '@dhis2/d2-i18n'
|
|
3
|
+
import {
|
|
4
|
+
Center,
|
|
5
|
+
CircularLoader,
|
|
6
|
+
DataTable,
|
|
7
|
+
DataTableCell,
|
|
8
|
+
DataTableColumnHeader,
|
|
9
|
+
DataTableRow,
|
|
10
|
+
NoticeBox,
|
|
11
|
+
TableBody,
|
|
12
|
+
TableHead,
|
|
13
|
+
} from '@dhis2/ui'
|
|
14
|
+
import * as React from 'react'
|
|
15
|
+
import classes from './DataElementsList.module.css'
|
|
16
|
+
interface QueryResults {
|
|
17
|
+
dataElements: Array<{
|
|
18
|
+
id: string
|
|
19
|
+
name: string
|
|
20
|
+
displayName: string
|
|
21
|
+
domainType: string
|
|
22
|
+
valueType: string
|
|
23
|
+
createdBy: {
|
|
24
|
+
id: string
|
|
25
|
+
name: string
|
|
26
|
+
}
|
|
27
|
+
}>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Documentation for the API: https://docs.dhis2.org/en/develop/using-the-api/dhis-core-version-master/metadata.html
|
|
32
|
+
* Documentation for useDataQuery hook: https://developers.dhis2.org/docs/tutorials/app-runtime-query/
|
|
33
|
+
* UI Library: https://developers.dhis2.org/docs/ui/webcomponents
|
|
34
|
+
*/
|
|
35
|
+
const PAGE_SIZE = 25
|
|
36
|
+
const query = {
|
|
37
|
+
dataElements: {
|
|
38
|
+
resource: 'dataElements',
|
|
39
|
+
params: {
|
|
40
|
+
pageSize: PAGE_SIZE,
|
|
41
|
+
fields: [
|
|
42
|
+
'id,name,displayName,domainType,valueType,categoryCombo[id,name],createdBy',
|
|
43
|
+
],
|
|
44
|
+
order: 'created:desc',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
const DataElementsList = () => {
|
|
49
|
+
const { error, loading, data } = useDataQuery<{
|
|
50
|
+
dataElements: QueryResults
|
|
51
|
+
}>(query)
|
|
52
|
+
|
|
53
|
+
if (error) {
|
|
54
|
+
return (
|
|
55
|
+
<NoticeBox error title={i18n.t('Error')}>
|
|
56
|
+
{i18n.t('Error loading the data elements')}
|
|
57
|
+
</NoticeBox>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div>
|
|
63
|
+
<h2>{i18n.t('Last {{n}} Data Elements:', { n: PAGE_SIZE })}</h2>
|
|
64
|
+
<div className={classes.tableContainer}>
|
|
65
|
+
<DataTable>
|
|
66
|
+
<TableHead>
|
|
67
|
+
<DataTableRow>
|
|
68
|
+
<DataTableColumnHeader large>
|
|
69
|
+
{i18n.t('ID')}
|
|
70
|
+
</DataTableColumnHeader>
|
|
71
|
+
<DataTableColumnHeader large>
|
|
72
|
+
{i18n.t('Name')}
|
|
73
|
+
</DataTableColumnHeader>
|
|
74
|
+
<DataTableColumnHeader large>
|
|
75
|
+
{i18n.t('Domain')}
|
|
76
|
+
</DataTableColumnHeader>
|
|
77
|
+
<DataTableColumnHeader
|
|
78
|
+
width="180px"
|
|
79
|
+
align="center"
|
|
80
|
+
large
|
|
81
|
+
>
|
|
82
|
+
{i18n.t('Value Type')}
|
|
83
|
+
</DataTableColumnHeader>
|
|
84
|
+
<DataTableColumnHeader large>
|
|
85
|
+
{i18n.t('Created by')}{' '}
|
|
86
|
+
</DataTableColumnHeader>
|
|
87
|
+
</DataTableRow>
|
|
88
|
+
</TableHead>
|
|
89
|
+
<TableBody>
|
|
90
|
+
{loading && (
|
|
91
|
+
<DataTableRow>
|
|
92
|
+
<DataTableCell colSpan="100%">
|
|
93
|
+
<Center>
|
|
94
|
+
<CircularLoader large />
|
|
95
|
+
</Center>
|
|
96
|
+
</DataTableCell>
|
|
97
|
+
</DataTableRow>
|
|
98
|
+
)}
|
|
99
|
+
{data?.dataElements?.dataElements?.map(
|
|
100
|
+
(dataElement) => {
|
|
101
|
+
return (
|
|
102
|
+
<DataTableRow key={dataElement.id}>
|
|
103
|
+
<DataTableCell large>
|
|
104
|
+
{dataElement.id}
|
|
105
|
+
</DataTableCell>
|
|
106
|
+
<DataTableCell large>
|
|
107
|
+
{dataElement.displayName}
|
|
108
|
+
</DataTableCell>
|
|
109
|
+
<DataTableCell large>
|
|
110
|
+
{dataElement.domainType}
|
|
111
|
+
</DataTableCell>
|
|
112
|
+
<DataTableCell large>
|
|
113
|
+
{dataElement.valueType?.replaceAll(
|
|
114
|
+
'_',
|
|
115
|
+
' '
|
|
116
|
+
)}
|
|
117
|
+
</DataTableCell>
|
|
118
|
+
<DataTableCell large>
|
|
119
|
+
{dataElement.createdBy.name}
|
|
120
|
+
</DataTableCell>
|
|
121
|
+
</DataTableRow>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
)}
|
|
125
|
+
</TableBody>
|
|
126
|
+
</DataTable>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export default DataElementsList
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"noEmit": true,
|
|
4
|
+
"skipLibCheck": true,
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"jsx": "react",
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"target": "ESNext",
|
|
9
|
+
"module": "esnext",
|
|
10
|
+
"moduleResolution": "node",
|
|
11
|
+
"baseUrl": ".",
|
|
12
|
+
"paths": {
|
|
13
|
+
"@/*": ["src/*"]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"include": ["src", "types"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { defineConfig, ConfigEnv } from 'vite'
|
|
3
|
+
|
|
4
|
+
const viteConfig = defineConfig(async (configEnv: ConfigEnv) => {
|
|
5
|
+
const { mode } = configEnv
|
|
6
|
+
return {
|
|
7
|
+
// In dev environments, don't clear the terminal after files update
|
|
8
|
+
clearScreen: mode !== 'development',
|
|
9
|
+
// Use an import alias: import from '@/' anywhere instead of 'src/'
|
|
10
|
+
resolve: { alias: { '@': path.resolve(__dirname, 'src') } },
|
|
11
|
+
// ...other config options here
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export default viteConfig
|
|
Binary file
|
|
Binary file
|