eionet2-dashboard 1.4.0

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.
Files changed (111) hide show
  1. package/.fx/configs/azure.parameters.Prod_EEA.json +15 -0
  2. package/.fx/configs/azure.parameters.dev.json +15 -0
  3. package/.fx/configs/config.Prod_EEA.json +10 -0
  4. package/.fx/configs/config.dev.json +10 -0
  5. package/.fx/configs/projectSettings.json +83 -0
  6. package/.fx/states/state.Prod_EEA.json +47 -0
  7. package/.fx/states/state.dev.json +47 -0
  8. package/.vscode/launch.json +91 -0
  9. package/.vscode/settings.json +6 -0
  10. package/.vscode/tasks.json +63 -0
  11. package/CHANGELOG.md +140 -0
  12. package/Jenkinsfile +166 -0
  13. package/LICENSE.md +9 -0
  14. package/README.md +55 -0
  15. package/api/.funcignore +11 -0
  16. package/api/extensions.csproj +11 -0
  17. package/api/getGraphData/function.json +27 -0
  18. package/api/getGraphData/index.js +147 -0
  19. package/api/host.json +11 -0
  20. package/api/package-lock.json +1546 -0
  21. package/api/package.json +17 -0
  22. package/api/proxies.json +4 -0
  23. package/package.json +25 -0
  24. package/tabs/.env.teamsfx.Prod_EEA +11 -0
  25. package/tabs/.env.teamsfx.dev +11 -0
  26. package/tabs/.eslintrc.json +48 -0
  27. package/tabs/.prettierrc +7 -0
  28. package/tabs/.stylelintrc.json +6 -0
  29. package/tabs/babel.config.js +3 -0
  30. package/tabs/package-lock.json +15564 -0
  31. package/tabs/package.json +88 -0
  32. package/tabs/public/auth-end.html +76 -0
  33. package/tabs/public/auth-start.html +178 -0
  34. package/tabs/public/deploy.png +0 -0
  35. package/tabs/public/favicon.ico +0 -0
  36. package/tabs/public/hello.png +0 -0
  37. package/tabs/public/index.html +20 -0
  38. package/tabs/public/publish.png +0 -0
  39. package/tabs/src/components/App.jsx +36 -0
  40. package/tabs/src/components/CustomColumnResizeIcon.jsx +68 -0
  41. package/tabs/src/components/CustomDrawer.jsx +51 -0
  42. package/tabs/src/components/EventDialogTitle.jsx +29 -0
  43. package/tabs/src/components/HtmlBox.jsx +18 -0
  44. package/tabs/src/components/Privacy.jsx +17 -0
  45. package/tabs/src/components/ResizableGrid.jsx +44 -0
  46. package/tabs/src/components/Tab.jsx +477 -0
  47. package/tabs/src/components/Tab.scss +138 -0
  48. package/tabs/src/components/TabConfig.jsx +51 -0
  49. package/tabs/src/components/TabPanel.jsx +29 -0
  50. package/tabs/src/components/TermsOfUse.jsx +17 -0
  51. package/tabs/src/components/UnderConstruction.jsx +24 -0
  52. package/tabs/src/components/UserMenu.jsx +109 -0
  53. package/tabs/src/components/_variables.scss +10 -0
  54. package/tabs/src/components/activity/Activity.jsx +301 -0
  55. package/tabs/src/components/activity/ConsultationList.jsx +297 -0
  56. package/tabs/src/components/activity/EventList.jsx +463 -0
  57. package/tabs/src/components/activity/GroupsTags.jsx +26 -0
  58. package/tabs/src/components/activity/Reporting.jsx +13 -0
  59. package/tabs/src/components/activity/activity.scss +153 -0
  60. package/tabs/src/components/event_rating/EventRating.jsx +92 -0
  61. package/tabs/src/components/event_rating/EventRatingDialog.jsx +46 -0
  62. package/tabs/src/components/event_registration/Approval.jsx +80 -0
  63. package/tabs/src/components/event_registration/ApprovalDialog.jsx +30 -0
  64. package/tabs/src/components/event_registration/ApprovalList.jsx +62 -0
  65. package/tabs/src/components/event_registration/EventRegistration.jsx +214 -0
  66. package/tabs/src/components/lib/useData.js +33 -0
  67. package/tabs/src/components/lib/useGraph.js +39 -0
  68. package/tabs/src/components/lib/useTeamsFx.js +55 -0
  69. package/tabs/src/components/my_country/AtAGlance.jsx +151 -0
  70. package/tabs/src/components/my_country/CountryProgress.jsx +92 -0
  71. package/tabs/src/components/my_country/DataReporters.jsx +13 -0
  72. package/tabs/src/components/my_country/GroupView.jsx +54 -0
  73. package/tabs/src/components/my_country/GroupsBoard.jsx +52 -0
  74. package/tabs/src/components/my_country/IndicatorCard.jsx +60 -0
  75. package/tabs/src/components/my_country/ManagementBoard.jsx +109 -0
  76. package/tabs/src/components/my_country/MyCountry.jsx +186 -0
  77. package/tabs/src/components/my_country/ProgressGauge.jsx +125 -0
  78. package/tabs/src/components/my_country/ScientificCommittee.jsx +13 -0
  79. package/tabs/src/components/my_country/YearlyProgress.jsx +41 -0
  80. package/tabs/src/components/my_country/my_country.scss +81 -0
  81. package/tabs/src/components/publications/Publications.jsx +13 -0
  82. package/tabs/src/components/self_service/UserEdit.jsx +334 -0
  83. package/tabs/src/components/self_service/UserEdit.scss +107 -0
  84. package/tabs/src/data/apiProvider.js +153 -0
  85. package/tabs/src/data/constants.json +7 -0
  86. package/tabs/src/data/hooks/useConfiguration.js +18 -0
  87. package/tabs/src/data/icsHelper.js +38 -0
  88. package/tabs/src/data/messages.json +39 -0
  89. package/tabs/src/data/provider.js +199 -0
  90. package/tabs/src/data/selfServiceProvider.js +59 -0
  91. package/tabs/src/data/selfServiceSharepointProvider.js +68 -0
  92. package/tabs/src/data/sharepointProvider.js +729 -0
  93. package/tabs/src/data/validator.js +25 -0
  94. package/tabs/src/data/validator.test.js +9 -0
  95. package/tabs/src/index.css +16 -0
  96. package/tabs/src/index.jsx +6 -0
  97. package/tabs/src/static/images/teams-icon.svg +1 -0
  98. package/tabs/src/utils/uiHelper.js +6 -0
  99. package/templates/appPackage/aad.template.json +133 -0
  100. package/templates/appPackage/manifest.template.json +58 -0
  101. package/templates/appPackage/resources/color.png +0 -0
  102. package/templates/appPackage/resources/outline.png +0 -0
  103. package/templates/azure/config.bicep +27 -0
  104. package/templates/azure/main.bicep +20 -0
  105. package/templates/azure/provision/frontendHosting.bicep +23 -0
  106. package/templates/azure/provision/function.bicep +82 -0
  107. package/templates/azure/provision/identity.bicep +14 -0
  108. package/templates/azure/provision/simpleAuth.bicep +44 -0
  109. package/templates/azure/provision.bicep +58 -0
  110. package/templates/azure/teamsFx/function.bicep +76 -0
  111. package/templates/azure/teamsFx/simpleAuth.bicep +43 -0
@@ -0,0 +1,334 @@
1
+ import { React, useState, useEffect } from 'react';
2
+ import { saveData } from '../../data/selfServiceProvider';
3
+ import { getGenderList } from '../../data/selfServiceSharepointProvider';
4
+ import { validateMandatoryField, validateName, validatePhone } from '../../data/validator';
5
+ import './UserEdit.scss';
6
+ import {
7
+ Box,
8
+ TextField,
9
+ Autocomplete,
10
+ Button,
11
+ FormLabel,
12
+ CircularProgress,
13
+ Chip,
14
+ Paper,
15
+ InputLabel,
16
+ Link,
17
+ Typography,
18
+ Divider,
19
+ } from '@mui/material';
20
+ import CheckIcon from '@mui/icons-material/Check';
21
+ import SaveIcon from '@mui/icons-material/Save';
22
+
23
+ export function UserEdit({ user }) {
24
+ const [loading, setLoading] = useState(false),
25
+ [success, setSuccess] = useState(false),
26
+ [warningVisible, setWarningVisible] = useState(false),
27
+ [warningText, setWarningText] = useState(''),
28
+ [genders, setGenders] = useState([]);
29
+
30
+ const [errors, setErrors] = useState({});
31
+
32
+ const submit = async (e) => {
33
+ if (!loading) {
34
+ e.preventDefault();
35
+ let tempErrors = validateForm();
36
+ setWarningVisible(false);
37
+ if (
38
+ !tempErrors ||
39
+ !Object.values(tempErrors).some((v) => {
40
+ return v;
41
+ })
42
+ ) {
43
+ setSuccess(false);
44
+ setLoading(true);
45
+ let result = await saveData(user);
46
+ if (!result.Success) {
47
+ setWarningText(result.Message + '\n' + result.Error);
48
+ setWarningVisible(true);
49
+ setSuccess(false);
50
+ } else {
51
+ setWarningText('');
52
+ setWarningVisible(false);
53
+ }
54
+ setSuccess(true);
55
+ setLoading(false);
56
+ }
57
+ }
58
+ },
59
+ validateField = (e) => {
60
+ let id = e.target.id,
61
+ tempErrors = { ...errors };
62
+
63
+ switch (id) {
64
+ case 'gender':
65
+ tempErrors.gender = validateName(user.Gender);
66
+ break;
67
+ case 'firstName':
68
+ tempErrors.firstName = validateName(user.FirstName);
69
+ break;
70
+ case 'lastName':
71
+ tempErrors.lastName = validateName(user.LastName);
72
+ break;
73
+ case 'phone':
74
+ tempErrors.phone = validatePhone(user.Phone);
75
+ break;
76
+
77
+ default:
78
+ console.log('Undefined field for validation');
79
+ break;
80
+ }
81
+
82
+ setErrors({ ...tempErrors });
83
+ },
84
+ validateForm = () => {
85
+ let tempErrors = { ...errors };
86
+ tempErrors.gender = validateMandatoryField(user.Gender);
87
+ tempErrors.firstName = validateName(user.FirstName);
88
+ tempErrors.lastName = validateName(user.LastName);
89
+ tempErrors.phone = validatePhone(user.Phone);
90
+ setErrors({ ...tempErrors });
91
+ return tempErrors;
92
+ };
93
+
94
+ useEffect(() => {
95
+ (async () => {
96
+ let loadedGenders = await getGenderList();
97
+ loadedGenders && setGenders(loadedGenders);
98
+ })();
99
+ });
100
+
101
+ return (
102
+ <div className="">
103
+ {user && (
104
+ <Box
105
+ sx={{
106
+ overflowY: 'scroll',
107
+ paddingLeft: '1.5rem',
108
+ paddingTop: '6rem',
109
+ height: '100%',
110
+ backgroundColor: 'suplementary.main',
111
+ }}
112
+ >
113
+ <Box
114
+ component="form"
115
+ sx={{
116
+ '& .MuiTextField-root': { m: 1 },
117
+ }}
118
+ autoComplete="off"
119
+ noValidate
120
+ onSubmit={(e) => {
121
+ submit(e);
122
+ }}
123
+ >
124
+ <Typography className="subtitle">Manage personal details</Typography>
125
+ <FormLabel className="note-label">
126
+ {user.SelfSeviceHelpdeskPersonalDetailsText}{' '}
127
+ </FormLabel>
128
+ <Box className="row-container" sx={{ backgroundColor: 'white', marginTop: '1.5rem' }}>
129
+ <Box className="row">
130
+ <Autocomplete
131
+ disablePortal
132
+ sx={{ width: '20ch', marginRight: '0.75rem' }}
133
+ id="combo-box-gender"
134
+ defaultValue={user.Gender || ''}
135
+ options={genders}
136
+ onChange={(e, value) => {
137
+ user.Gender = value;
138
+ }}
139
+ renderInput={(params) => (
140
+ <TextField
141
+ {...params}
142
+ autoComplete="off"
143
+ className="control"
144
+ label="Salutation"
145
+ variant="standard"
146
+ error={Boolean(errors?.gender)}
147
+ helperText={errors?.gender}
148
+ onBlur={validateField}
149
+ />
150
+ )}
151
+ />
152
+ <TextField
153
+ required
154
+ autoComplete="off"
155
+ className="control"
156
+ id="firstName"
157
+ label="First name"
158
+ variant="standard"
159
+ defaultValue={user.FirstName}
160
+ onChange={(e) => {
161
+ user.FirstName = e.target.value;
162
+ validateField(e);
163
+ }}
164
+ inputProps={{ style: { textTransform: 'capitalize' } }}
165
+ error={Boolean(errors?.firstName)}
166
+ helperText={errors?.firstName}
167
+ onBlur={validateField}
168
+ />
169
+ <TextField
170
+ required
171
+ autoComplete="off"
172
+ className="control"
173
+ id="lastName"
174
+ label="Last name"
175
+ variant="standard"
176
+ defaultValue={user.LastName}
177
+ onChange={(e) => {
178
+ user.LastName = e.target.value;
179
+ validateField(e);
180
+ }}
181
+ inputProps={{ style: { textTransform: 'capitalize' } }}
182
+ error={Boolean(errors?.lastName)}
183
+ helperText={errors?.lastName}
184
+ onBlur={validateField}
185
+ />
186
+ <TextField
187
+ autoComplete="off"
188
+ className="control"
189
+ id="phone"
190
+ label="Phone"
191
+ variant="standard"
192
+ defaultValue={user.Phone}
193
+ onChange={(e) => {
194
+ user.Phone = e.target.value;
195
+ validateField(e);
196
+ }}
197
+ inputProps={{ maxLength: 15 }}
198
+ error={Boolean(errors?.phone)}
199
+ helperText={errors?.phone}
200
+ onBlur={validateField}
201
+ />
202
+ </Box>
203
+ <Box className="row">
204
+ <TextField
205
+ disabled
206
+ required
207
+ autoComplete="off"
208
+ className="control"
209
+ id="lastName"
210
+ label="Country"
211
+ variant="standard"
212
+ defaultValue={user.Country}
213
+ />
214
+ <TextField
215
+ disabled
216
+ required
217
+ autoComplete="off"
218
+ className="control"
219
+ id="email"
220
+ defaultValue={user.Email}
221
+ label="Email"
222
+ variant="standard"
223
+ />
224
+ <TextField
225
+ disabled
226
+ required
227
+ autoComplete="off"
228
+ className="control"
229
+ id="lastName"
230
+ label="Organisation"
231
+ variant="standard"
232
+ defaultValue={user.Organisation}
233
+ />
234
+ </Box>
235
+ </Box>
236
+ <Box className="row">
237
+ {user.Memberships && (
238
+ <Paper square className="paper-container" elevation={0}>
239
+ <InputLabel className="inputLabel">Memberships</InputLabel>
240
+ <Paper className="paper" elevation={0}>
241
+ {user.Memberships.map((data) => {
242
+ return (
243
+ <Chip
244
+ variant="outlined"
245
+ color="primary"
246
+ key={data}
247
+ className="chip"
248
+ label={data}
249
+ />
250
+ );
251
+ })}
252
+ </Paper>
253
+ </Paper>
254
+ )}
255
+ {user.OtherMemberships && (
256
+ <Paper square className="paper-container" elevation={0}>
257
+ <InputLabel className="inputLabel">Other memberships</InputLabel>
258
+ <Paper className="paper" elevation={0}>
259
+ {user.OtherMemberships.map((data) => {
260
+ return (
261
+ <Chip
262
+ variant="outlined"
263
+ color="primary"
264
+ key={data}
265
+ className="chip"
266
+ label={data}
267
+ />
268
+ );
269
+ })}
270
+ </Paper>
271
+ </Paper>
272
+ )}
273
+ {user.NFP && (
274
+ <Paper sx={{ marginRight: '0' }} square className="paper-container" elevation={0}>
275
+ <InputLabel className="inputLabel">NFP</InputLabel>
276
+ <Paper className="paper" elevation={0}>
277
+ <Chip className="chip" variant="outlined" color="primary" label={user.NFP} />
278
+ </Paper>
279
+ </Paper>
280
+ )}
281
+ </Box>
282
+ <div className="row">
283
+ <FormLabel className="note-label">
284
+ Note: If the email or other details needs to be changed, kindly contact{' '}
285
+ <Link sx={{ color: 'secondary.main' }} href="mailto:helpdesk@eea.europa.eu">
286
+ EEA Helpdesk
287
+ </Link>
288
+ .
289
+ </FormLabel>
290
+ </div>
291
+ <div className="row">
292
+ <Box sx={{ position: 'relative' }}>
293
+ <Button
294
+ type="submit"
295
+ variant="contained"
296
+ color="primary"
297
+ size="medium"
298
+ className="button"
299
+ disabled={loading}
300
+ endIcon={success ? <CheckIcon /> : <SaveIcon />}
301
+ >
302
+ Save changes
303
+ </Button>
304
+ {loading && (
305
+ <CircularProgress
306
+ color="primary"
307
+ size={24}
308
+ sx={{
309
+ position: 'absolute',
310
+ top: '50%',
311
+ left: '50%',
312
+ marginTop: '-12px',
313
+ marginLeft: '-12px',
314
+ }}
315
+ />
316
+ )}
317
+ </Box>
318
+ {warningVisible && (
319
+ <FormLabel className="note-label warning" error>
320
+ {warningText}
321
+ </FormLabel>
322
+ )}
323
+ </div>
324
+ </Box>
325
+ <Divider sx={{ marginBottom: '1rem' }}></Divider>
326
+ <Box>
327
+ <Typography className="subtitle">Manage preferences</Typography>
328
+ <FormLabel className="note-label">{user.SelfSeviceHelpdeskPreferencesText} </FormLabel>
329
+ </Box>
330
+ </Box>
331
+ )}
332
+ </div>
333
+ );
334
+ }
@@ -0,0 +1,107 @@
1
+ @import '../variables.scss';
2
+
3
+ .error {
4
+ color: red;
5
+ }
6
+
7
+ .row-container {
8
+ background-color: white;
9
+ width: 65%;
10
+
11
+ @media (max-width: map-get($breakpoints, lg)) {
12
+ flex-direction: column;
13
+ width: 100%;
14
+ }
15
+
16
+ .row {
17
+ width: 100%;
18
+ }
19
+ }
20
+
21
+ .row {
22
+ padding: 0.2rem;
23
+ display: flex;
24
+ flex-direction: row;
25
+ width: 65%;
26
+ margin-bottom: 0.5rem;
27
+
28
+ @media (max-width: map-get($breakpoints, lg)) {
29
+ flex-direction: column;
30
+ width: 100%;
31
+ }
32
+ }
33
+
34
+ .control {
35
+ margin-left: 0.75rem !important;
36
+ margin-right: 0.75rem !important;
37
+
38
+ @media (max-width: map-get($breakpoints, lg)) {
39
+ width: 100% !important;
40
+ }
41
+ }
42
+
43
+ .inputLabel {
44
+ margin: 0.5rem;
45
+ font-size: 14px !important;
46
+ font-weight: 500 !important;
47
+ }
48
+
49
+ .paper-container {
50
+ background-color: white;
51
+ width: 33%;
52
+ margin-right: 0.5rem;
53
+ margin-bottom: 0.5rem;
54
+
55
+ @media (max-width: map-get($breakpoints, lg)) {
56
+ width: 100% !important;
57
+ }
58
+ }
59
+
60
+ .paper {
61
+ margin: 8px !important;
62
+ width: 90%;
63
+ display: flex;
64
+ flex-direction: column;
65
+ justify-content: flex-start;
66
+ flex-wrap: wrap;
67
+ background-color: white;
68
+ }
69
+
70
+ .chip {
71
+ margin: 5px;
72
+ display: flex;
73
+ width: fit-content;
74
+ justify-content: center;
75
+ flex-wrap: wrap;
76
+ }
77
+
78
+ .success {
79
+ color: green;
80
+ font-weight: bold;
81
+ padding: 0.35rem;
82
+ }
83
+
84
+ .user-list {
85
+ width: 100%;
86
+ height: 50%;
87
+ }
88
+
89
+ .button {
90
+ margin: 1rem 0 0 0.5rem;
91
+ position: relative;
92
+ }
93
+
94
+ .small-width {
95
+ width: 25% !important;
96
+ min-width: 150px !important;
97
+ }
98
+
99
+ .subtitle {
100
+ font-size: 18px !important;
101
+ font-weight: 600 !important;
102
+ }
103
+
104
+ .note-label {
105
+ margin-left: 0;
106
+ font-size: 16px !important;
107
+ }
@@ -0,0 +1,153 @@
1
+ import { TeamsUserCredential, getResourceConfiguration, ResourceType } from '@microsoft/teamsfx';
2
+ import * as axios from 'axios';
3
+
4
+ async function callApiFunction(command, method, options, params) {
5
+ let message = [];
6
+
7
+ const credential = new TeamsUserCredential();
8
+ const accessToken = await credential.getToken('');
9
+ const apiConfig = getResourceConfiguration(ResourceType.API);
10
+ const response = await axios.default.request({
11
+ method: method,
12
+ url: apiConfig.endpoint + '/api/' + command,
13
+ headers: {
14
+ authorization: 'Bearer ' + accessToken.token,
15
+ },
16
+ data: options,
17
+ params,
18
+ });
19
+ message = response.data;
20
+
21
+ return message;
22
+ }
23
+
24
+ export async function apiGet(path, credentialType = 'app') {
25
+ try {
26
+ return await callApiFunction('graphData', 'get', undefined, {
27
+ path: path,
28
+ credentialType: credentialType,
29
+ });
30
+ } catch (err) {
31
+ logError(err, path, null);
32
+ throw err;
33
+ }
34
+ }
35
+
36
+ export async function apiPost(path, data, credentialType = 'app') {
37
+ try {
38
+ return await callApiFunction('graphData', 'post', {
39
+ credentialType: credentialType,
40
+ data: data,
41
+ path: path,
42
+ });
43
+ } catch (err) {
44
+ logError(err, path, data);
45
+ throw err;
46
+ }
47
+ }
48
+
49
+ export async function apiPatch(path, data, eTag = undefined, credentialType = 'app') {
50
+ try {
51
+ return await callApiFunction('graphData', 'patch', {
52
+ credentialType: credentialType,
53
+ data: data,
54
+ path: path,
55
+ ...(eTag && { eTag: eTag }),
56
+ });
57
+ } catch (err) {
58
+ logError(err, path, data);
59
+ throw err;
60
+ }
61
+ }
62
+
63
+ export async function apiDelete(path, credentialType = 'app') {
64
+ try {
65
+ return await callApiFunction('graphData', 'delete', {
66
+ credentialType: credentialType,
67
+ path: path,
68
+ });
69
+ } catch (err) {
70
+ logError(err, path, null);
71
+ throw err;
72
+ }
73
+ }
74
+
75
+ let _userMail = undefined;
76
+ export async function getUserMail() {
77
+ if (!_userMail) {
78
+ const response = await apiGet('me?$select=id,displayName,mail,mobilePhone,country', 'user');
79
+
80
+ if (response.graphClientMessage) {
81
+ _userMail = response.graphClientMessage.mail;
82
+ }
83
+ }
84
+ return _userMail;
85
+ }
86
+
87
+ const sharepointSiteId = process.env.REACT_APP_SHAREPOINT_SITE_ID,
88
+ configurationListId = process.env.REACT_APP_CONFIGURATION_LIST_ID;
89
+
90
+ let _configuration = undefined;
91
+ export async function getConfiguration() {
92
+ try {
93
+ if (!_configuration) {
94
+ const response = await apiGet(
95
+ '/sites/' + sharepointSiteId + '/lists/' + configurationListId + '/items?$expand=fields',
96
+ );
97
+ _configuration = {};
98
+ response.graphClientMessage.value.forEach(function (item) {
99
+ _configuration[item.fields.Title] = item.fields.Value;
100
+ });
101
+ _configuration.SharepointSiteId = sharepointSiteId;
102
+ }
103
+ return _configuration;
104
+ } catch (err) {
105
+ console.log(err);
106
+ return undefined;
107
+ }
108
+ }
109
+
110
+ export async function logError(err, apiPath, data) {
111
+ const spConfig = await getConfiguration(),
112
+ userMail = await getUserMail();
113
+
114
+ const title = err.response?.data?.error?.body;
115
+
116
+ let fields = {
117
+ fields: {
118
+ ApplicationName: 'Eionet2-Dashboard',
119
+ ApiPath: apiPath,
120
+ ApiData: JSON.stringify(data),
121
+ Title: title ? title : err,
122
+ UserMail: userMail,
123
+ Timestamp: new Date(),
124
+ Logtype: 'Error',
125
+ },
126
+ };
127
+
128
+ let graphURL =
129
+ '/sites/' + spConfig.SharepointSiteId + '/lists/' + spConfig.LoggingListId + '/items';
130
+ await apiPost(graphURL, fields);
131
+ }
132
+
133
+ export async function logInfo(message, apiPath, data, action) {
134
+ const spConfig = await getConfiguration(),
135
+ userMail = await getUserMail();
136
+
137
+ let fields = {
138
+ fields: {
139
+ ApplicationName: 'Eionet2-Dashboard',
140
+ ApiPath: apiPath,
141
+ ApiData: JSON.stringify(data),
142
+ Title: message,
143
+ UserMail: userMail,
144
+ Timestamp: new Date(),
145
+ Logtype: 'Info',
146
+ Action: action,
147
+ },
148
+ };
149
+
150
+ let graphURL =
151
+ '/sites/' + spConfig.SharepointSiteId + '/lists/' + spConfig.LoggingListId + '/items';
152
+ await apiPost(graphURL, fields);
153
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "GridRowHeight": 40,
3
+ "ConsultationType": {
4
+ "Consultation": "Consultation",
5
+ "Survey": "Inquiry"
6
+ }
7
+ }
@@ -0,0 +1,18 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { getConfiguration } from '../apiProvider';
3
+
4
+ export function useConfiguration() {
5
+ const [configuration, setConfiguration] = useState({});
6
+
7
+ useEffect(() => {
8
+ async function fetchData() {
9
+ let loadedConfiguration = await getConfiguration();
10
+ if (loadedConfiguration) {
11
+ setConfiguration(loadedConfiguration);
12
+ }
13
+ }
14
+ fetchData();
15
+ }, [getConfiguration]);
16
+
17
+ return configuration;
18
+ }
@@ -0,0 +1,38 @@
1
+ import { createEvent } from 'ics';
2
+ import { differenceInMinutes } from 'date-fns';
3
+
4
+ export function createIcs(meeting) {
5
+ let result = undefined,
6
+ duration = undefined;
7
+ const meetingStart = new Date(meeting.MeetingStart);
8
+ const durationInMinutes = differenceInMinutes(new Date(meeting.MeetingEnd), meetingStart);
9
+ if (durationInMinutes >= 60) {
10
+ duration = {
11
+ hours: Math.floor(durationInMinutes / 60),
12
+ minutes: durationInMinutes % 60,
13
+ };
14
+ } else {
15
+ duration = {
16
+ minutes: durationInMinutes,
17
+ };
18
+ }
19
+ const event = {
20
+ start: [
21
+ meetingStart.getFullYear(),
22
+ meetingStart.getMonth(),
23
+ meetingStart.getDate(),
24
+ meetingStart.getHours(),
25
+ meetingStart.getMinutes(),
26
+ ],
27
+ duration: duration,
28
+ title: meeting.Title,
29
+ ...(meeting.MeetingLink && { url: meeting.MeetingLink }),
30
+ };
31
+
32
+ createEvent(event, (error, value) => {
33
+ const blob = new Blob([value], { type: 'text/plain;charset=utf-8' });
34
+ result = blob;
35
+ });
36
+
37
+ return result;
38
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "UserInvite": {
3
+ "UserAlreadyRegistered": "User with this email already registered.",
4
+ "InvalidEmail": "Invalid email. Please correct the email address!",
5
+ "EEAUserError": "Users with EEA email address cannot be invited as guests.",
6
+ "Errors": {
7
+ "Invitation": "Could not create the user invitation.",
8
+ "ADUserCreation": "Could not update the user contact data.",
9
+ "JoiningTeam": "The user could not be added to the Eionet teams. Please use the Manage user tab to update the user Membership(s)",
10
+ "TagsCreation": "The tags could not be updated. Please use the Manage user tab to update the user Membership(s)",
11
+ "SharepointUser": "User details could not be updated in the SharePoint Users list.",
12
+ "Mail": "An error has occured during the sending invitation email.",
13
+ "Error": "An error has occured during the invitation process."
14
+ }
15
+ },
16
+ "UserList": {
17
+ "MissingADUser": "The user account cannot be updated (account is not present in the Azure tenant).",
18
+ "DeleteUser": "Are you sure you want to remove the user #name# from the Eionet directory?",
19
+ "DeleteUserMemberships": "The user is also a member of the following groups at EEA:"
20
+ },
21
+ "UserEdit": {
22
+ "MissingMembership": "At least one the following fields must be selected Eionet groups, Other memberships or NFP.",
23
+ "Errors": {
24
+ "ADUserCreation": "Could not update the user contact data.",
25
+ "Mail": "An error has occured during the sending inviation email.",
26
+ "Invitation": "Could not create the user invitation.",
27
+ "ADUser": "Could not update the user contact data.",
28
+ "JoiningTeam": "The user could not be added to the Eionet teams. Please use the Manage user tab to update the user Membership(s)",
29
+ "TagsCreation": "The tags could not be updated. Please use the Manage user tab to update the user Membership(s)",
30
+ "SharepointUser": "User details could not be updated in the SharePoint Users list.",
31
+ "Error": "An error has occured when saving user information."
32
+ }
33
+ },
34
+ "UserDelete": {
35
+ "Errors": {
36
+ "ADUser": "Could not update the user information."
37
+ }
38
+ }
39
+ }