eionet2-dashboard 1.8.0 → 1.8.2

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/CHANGELOG.md CHANGED
@@ -4,7 +4,22 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
- ### [1.8.0](https://github.com/eea/eionet2-dashboard/compare/1.7.1...1.8.0) - 8 December 2023
7
+ ### [1.8.2](https://github.com/eea/eionet2-dashboard/compare/1.8.1...1.8.2) - 23 January 2024
8
+
9
+ #### :rocket: New Features
10
+
11
+ - feat: misc changes [Mihai Nicolae - [`ddaeb45`](https://github.com/eea/eionet2-dashboard/commit/ddaeb4545741d9137d064ed7167d5a0c1235be0b)]
12
+ - feat: small changes(#262918) [Mihai Nicolae - [`bba1224`](https://github.com/eea/eionet2-dashboard/commit/bba122453a863f66d1d8aadf6aab2ea3e65913a1)]
13
+
14
+ #### :house: Internal changes
15
+
16
+ - chore: node version fix [Mihai Nicolae - [`2b79d49`](https://github.com/eea/eionet2-dashboard/commit/2b79d493e2fa6625c4629cc3d81afa7be0c2a96a)]
17
+ - chore: new version [Mihai Nicolae - [`b0baa55`](https://github.com/eea/eionet2-dashboard/commit/b0baa55a3545b36a2be4f552423d243501d4bcf2)]
18
+ - chore: new config key [Mihai Nicolae - [`d1bcf33`](https://github.com/eea/eionet2-dashboard/commit/d1bcf3353c2a3ec63a7b668cd4013d25757a7d22)]
19
+
20
+ ### [1.8.1](https://github.com/eea/eionet2-dashboard/compare/1.8.0...1.8.1) - 11 December 2023
21
+
22
+ ### [1.8.0](https://github.com/eea/eionet2-dashboard/compare/1.7.1...1.8.0) - 11 December 2023
8
23
 
9
24
  #### :rocket: New Features
10
25
 
package/CONFIGURATION.md CHANGED
@@ -9,4 +9,12 @@ Configuration keys
9
9
  - ObligationsListId - Id of the sharepoint list that stores the obligations
10
10
  - ReportingClientsUrl - URL to the reporting obligations database site for client list
11
11
  - ReportingInstrumentsUrl - URL to the reporting obligations database site for instrument list
12
- - ReportingObligationsUrl - URL to the reporting obligations database site for obligation list. Used in dashboard and azure-jobs.
12
+ - ReportingObligationsUrl - URL to the reporting obligations database site for obligation list. Used in dashboard and azure-jobs.
13
+
14
+ ### [1.8.1]
15
+ Configuration keys
16
+ - InquiryListUrl - The link the consultations list filtered to display only enquiries. Used in the bottom bar buttons.
17
+
18
+ ### [1.8.2]
19
+ Configuration keys
20
+ - DashboardNoOfDisplayedYears - The numbers of years used in the Country overview to display information about progress.
package/README.md CHANGED
@@ -18,7 +18,7 @@ The data displayed is retrieved from the EEA Azure tenant and from an internal E
18
18
  This functionality is available for all users. In this tab, the users can find information about (and register to):
19
19
  - Events (that can be rated).
20
20
  - Consultations.
21
- - Inquiries.
21
+ - Enquiries.
22
22
  - Reports.
23
23
 
24
24
  The displayed information is a selected sub-type of the full information list, ordered by (expiration/closing) date. The full list can be accessed from the tab through a link, which will point the user to the corresponding repository folder(s) stored in SharePoint.
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "description": {
22
22
  "short": "Eionet Dashboard",
23
- "full": "Available to all EEA and Eionet users. The Eionet Dashboard shows shows relevant country statistics and information about all Eionet Groups and ETCs. It includes a list of all events, consultations, inquiries and other relevant information. It is needed by EIonet users to manage their registrations for Eionet events."
23
+ "full": "Available to all EEA and Eionet users. The Eionet Dashboard shows shows relevant country statistics and information about all Eionet Groups and ETCs. It includes a list of all events, consultations, enquiries and other relevant information. It is needed by EIonet users to manage their registrations for Eionet events."
24
24
  },
25
25
  "accentColor": "#FFFFFF",
26
26
  "bots": [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eionet2-dashboard",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "scripts": {
@@ -22,4 +22,4 @@
22
22
  "@fluentui/react-teams": "^6.0.0",
23
23
  "@mui/material": "^5.5.0"
24
24
  }
25
- }
25
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/eionet2-dashboard",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
package/tabs/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/eionet2-dashboard",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "description": "MS Teams app for accessing Eionet activity and managing account information.",
6
6
  "dependencies": {
@@ -85,5 +85,8 @@
85
85
  "node_modules/(?!@ngrx|(?!deck.gl)|ng-dynamic)"
86
86
  ]
87
87
  },
88
+ "resolutions": {
89
+ "@azure/core-rest-pipeline": "1.12.1"
90
+ },
88
91
  "homepage": "."
89
- }
92
+ }
@@ -34,6 +34,11 @@ export default function ResizableGrid(props) {
34
34
  components={{
35
35
  ColumnResizeIcon: ColumnResizeIcon,
36
36
  }}
37
+ componentsProps={{
38
+ panel: {
39
+ placement: 'auto',
40
+ },
41
+ }}
37
42
  columns={dataColumns}
38
43
  getRowHeight={() => {
39
44
  return Constants.GridRowHeight;
@@ -150,7 +150,7 @@ export default function Tab() {
150
150
 
151
151
  setSelectedCountry(me.country);
152
152
 
153
- if (me.isAdmin) {
153
+ if (me.isAdmin || !me.isEionetUser) {
154
154
  setCanChangeCountry(true);
155
155
  const loadedCountries = await getCountries();
156
156
  loadedCountries && setCountries(loadedCountries);
@@ -459,10 +459,10 @@ export default function Tab() {
459
459
  variant="outlined"
460
460
  endIcon={<OpenInNewIcon color="primary" />}
461
461
  onClick={() => {
462
- window.open(configuration.ConsultationListUrl, '_blank');
462
+ window.open(configuration.InquiryListUrl, '_blank');
463
463
  }}
464
464
  >
465
- All inquiries
465
+ All enquiries
466
466
  </Button>
467
467
  <Button
468
468
  className="bottom-button"
@@ -153,7 +153,7 @@ export function Activity({
153
153
  <ListItem disablePadding className="list-item" key={9}>
154
154
  <ListItemText
155
155
  className="list-item-text"
156
- primary={'INQUIRIES'}
156
+ primary={'ENQUIRIES'}
157
157
  sx={{ color: 'primary.main' }}
158
158
  />
159
159
  </ListItem>
@@ -143,13 +143,13 @@ export function ConsultationList({
143
143
  renderCell: renderStartDate,
144
144
  },
145
145
  titleColumn = {
146
- field: 'Closed',
146
+ field: 'Title',
147
147
  headerName: type,
148
148
  flex: 0.75,
149
149
  renderCell: renderConsultationTitle,
150
150
  },
151
151
  documentColumn = {
152
- field: 'Title',
152
+ field: 'Closed',
153
153
  headerName: 'Details',
154
154
  width: '100',
155
155
  align: 'center',
@@ -33,14 +33,15 @@ export function AtAGlance({
33
33
  setLoading(true);
34
34
 
35
35
  //get meetings from last two years
36
- const fromDate = new Date(new Date().getFullYear() - 2, 0, 1);
36
+ const noOfYears = configuration.DashboardNoOfDisplayedYears || 2;
37
+ const fromDate = new Date(new Date().getFullYear() - noOfYears, 0, 1);
37
38
  let loadedMeetings = await getMeetings(fromDate, country, userInfo),
38
39
  loadedConsultations = await getConsultations(undefined, fromDate);
39
40
 
40
41
  const current = new Date().getFullYear();
41
42
  let years = [];
42
- for (let i = current; i >= current - 1; i--) {
43
- const allMeetings = loadedMeetings.filter((m) => m.Year == i),
43
+ for (let i = current; i >= current - noOfYears + 1; i--) {
44
+ const allMeetings = loadedMeetings.filter((m) => m.Year == i && m.IsPast),
44
45
  allConsultations = loadedConsultations.filter(
45
46
  (c) => c.Year == i && c.ConsultationType == 'Consultation',
46
47
  ),
@@ -48,19 +49,25 @@ export function AtAGlance({
48
49
  (c) => c.Year == i && c.ConsultationType == 'Inquiry',
49
50
  );
50
51
 
52
+ const yearFilter = `&FilterField2=Year&FilterValue2=${i}&FilterType2=Number`;
51
53
  const result = {
52
54
  year: i,
53
55
  meetingsCount: allMeetings.length,
54
- consultationsCount: allConsultations.length,
55
- surveysCount: allSurveys.length,
56
+ meetingsUrl: `${configuration.MeetingListUrl}?FilterField1=Countries&FilterValue1=${country}${yearFilter}`,
56
57
  attendedMeetingsCount: allMeetings.filter((meeting) => {
57
58
  return meeting.Participants.some(
58
59
  (participant) => participant.Country == country && participant.Participated,
59
60
  );
60
61
  }).length,
62
+ consultationsCount: allConsultations.length,
63
+ //!!! ConsultationListUrl already contains a filter in configuration
64
+ consultationsUrl: `${configuration.ConsultationListUrl}${yearFilter}&FilterField3=Respondants&FilterValue3=${country}`,
61
65
  responseConsultationsCount: allConsultations.filter((c) => {
62
66
  return c.Respondants.includes(country);
63
67
  }).length,
68
+ surveysCount: allSurveys.length,
69
+ //!!! InquiryListUrl already contains a filter in configuration
70
+ surveysUrl: `${configuration.InquiryListUrl}${yearFilter}&FilterField3=Respondants&FilterValue3=${country}`,
64
71
  responseSurveysCount: allSurveys.filter((c) => {
65
72
  return c.Respondants.includes(country);
66
73
  }).length,
@@ -71,7 +78,7 @@ export function AtAGlance({
71
78
  setLoading(false);
72
79
  };
73
80
  fetchData();
74
- }, [country, userInfo]);
81
+ }, [country, userInfo, configuration]);
75
82
 
76
83
  return (
77
84
  <div className="">
@@ -69,16 +69,7 @@ export function CountryProgress({ lastYears, configuration }) {
69
69
  {lastYears.map((year, index) => {
70
70
  return (
71
71
  <TabPanel className="year-panel" key={index} value={tabsValue} index={index}>
72
- <YearlyProgress
73
- allMeetingsCount={year.meetingsCount}
74
- allConsultationsCount={year.consultationsCount}
75
- allSurveysCount={year.surveysCount}
76
- attendedMeetingsCount={year.attendedMeetingsCount}
77
- responseConsultationsCount={year.responseConsultationsCount}
78
- responseSurveysCount={year.responseSurveysCount}
79
- year={year.year}
80
- configuration={configuration}
81
- ></YearlyProgress>
72
+ <YearlyProgress yearData={year} configuration={configuration}></YearlyProgress>
82
73
  </TabPanel>
83
74
  );
84
75
  })}
@@ -1,10 +1,19 @@
1
1
  import { React, useState } from 'react';
2
- import { Box, Button, Card, CardContent, Typography, IconButton, Dialog } from '@mui/material';
2
+ import {
3
+ Box,
4
+ Button,
5
+ Card,
6
+ CardContent,
7
+ Typography,
8
+ IconButton,
9
+ Dialog,
10
+ Link,
11
+ } from '@mui/material';
3
12
  import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
4
13
 
5
14
  import { FullCircularProgress } from './FullCircularProgress';
6
15
 
7
- export function ProgressGauge({ totalCount, responseCount, label, infoText }) {
16
+ export function ProgressGauge({ totalCount, responseCount, label, infoText, url }) {
8
17
  const [infoOpen, setInfoOpen] = useState(false),
9
18
  handleInfoOpen = () => {
10
19
  infoText && setInfoOpen(true);
@@ -74,7 +83,7 @@ export function ProgressGauge({ totalCount, responseCount, label, infoText }) {
74
83
  </Typography>
75
84
  </Box>
76
85
  </Box>
77
- <Typography
86
+ <Link
78
87
  sx={{
79
88
  textAlign: 'center',
80
89
  marginTop: '1rem',
@@ -82,9 +91,12 @@ export function ProgressGauge({ totalCount, responseCount, label, infoText }) {
82
91
  height: '1rem',
83
92
  fontSize: '20px',
84
93
  }}
94
+ onClick={() => {
95
+ url && window.open(url, '_blank');
96
+ }}
85
97
  >
86
98
  {label}
87
- </Typography>
99
+ </Link>
88
100
  </CardContent>
89
101
  </Card>
90
102
  );
@@ -2,15 +2,7 @@ import { React } from 'react';
2
2
  import { Box, Typography } from '@mui/material';
3
3
  import { ProgressGauge } from './ProgressGauge';
4
4
 
5
- export function YearlyProgress({
6
- allMeetingsCount,
7
- attendedMeetingsCount,
8
- allConsultationsCount,
9
- responseConsultationsCount,
10
- allSurveysCount,
11
- responseSurveysCount,
12
- configuration,
13
- }) {
5
+ export function YearlyProgress({ yearData, configuration }) {
14
6
  return (
15
7
  <div className="">
16
8
  <Typography className="subtitle" color="text.secondary">
@@ -19,21 +11,24 @@ export function YearlyProgress({
19
11
  <Box className="cards-container" sx={{ border: '0px' }}>
20
12
  <ProgressGauge
21
13
  label="Consultations"
22
- totalCount={allConsultationsCount}
23
- responseCount={responseConsultationsCount}
14
+ totalCount={yearData.consultationsCount}
15
+ responseCount={yearData.responseConsultationsCount}
24
16
  infoText={configuration.YearlyConsultationsCountInfo}
17
+ url={yearData.consultationsUrl}
25
18
  ></ProgressGauge>
26
19
  <ProgressGauge
27
- label="Inquiries"
28
- totalCount={allSurveysCount}
29
- responseCount={responseSurveysCount}
20
+ label="Enquiries"
21
+ totalCount={yearData.surveysCount}
22
+ responseCount={yearData.responseSurveysCount}
30
23
  infoText={configuration.YearlySurveysCountInfo}
24
+ url={yearData.surveysUrl}
31
25
  ></ProgressGauge>
32
26
  <ProgressGauge
33
27
  label="Events"
34
- totalCount={allMeetingsCount}
35
- responseCount={attendedMeetingsCount}
28
+ totalCount={yearData.meetingsCount}
29
+ responseCount={yearData.attendedMeetingsCount}
36
30
  infoText={configuration.YearlyEventsCountInfo}
31
+ url={yearData.meetingsUrl}
37
32
  ></ProgressGauge>
38
33
  </Box>
39
34
  </div>
@@ -159,35 +159,37 @@ export async function getConsultations(consultationType, fromDate, userCountry)
159
159
 
160
160
  const itemLinkOperator = config.ConsultationListItemUrl.includes('?') ? '&' : '?';
161
161
  return consultations.value.map(function (consultation) {
162
- const respondants = consultation.fields.Respondants || [],
162
+ const fields = consultation.fields,
163
+ respondants = fields.Respondants || [],
163
164
  hasUserCountryResponded = userCountry && respondants.includes(userCountry);
165
+
164
166
  return {
165
- id: consultation.fields.id,
167
+ id: fields.id,
166
168
 
167
- Title: consultation.fields.Title,
168
- ConsultationType: consultation.fields.ConsultationType,
169
- Description: consultation.fields.Description,
169
+ Title: fields.Title,
170
+ ConsultationType: fields.ConsultationType,
171
+ Description: fields.Description,
170
172
 
171
- Startdate: new Date(consultation.fields.Startdate),
172
- Closed: new Date(consultation.fields.Closed),
173
- Deadline: new Date(consultation.fields.Deadline),
174
- Year: new Date(consultation.fields.Startdate).getFullYear(),
175
- DaysLeft: differenceInDays(new Date(consultation.fields.Closed), currentDate),
176
- DaysFinalised: differenceInDays(new Date(consultation.fields.Deadline), currentDate),
173
+ Startdate: new Date(fields.Startdate),
174
+ Closed: new Date(fields.Closed),
175
+ Deadline: new Date(fields.Deadline),
176
+ Year: parseInt(fields.Year.replace(',', '')),
177
+ DaysLeft: differenceInDays(new Date(fields.Closed), currentDate),
178
+ DaysFinalised: differenceInDays(new Date(fields.Deadline), currentDate),
177
179
 
178
- Linktofolder: consultation.fields.LinktoFolder,
180
+ Linktofolder: fields.LinktoFolder,
179
181
  Respondants: respondants,
180
182
  HasUserCountryResponded: hasUserCountryResponded,
181
- Countries: consultation.fields.Countries,
183
+ Countries: fields.Countries,
182
184
 
183
- ConsulationmanagerLookupId: consultation.fields.ConsulationmanagerLookupId,
184
- EionetGroups: consultation.fields.EionetGroups,
185
- LinkToResults: consultation.fields.LinkToResults,
185
+ ConsulationmanagerLookupId: fields.ConsulationmanagerLookupId,
186
+ EionetGroups: fields.EionetGroups,
187
+ LinkToResults: fields.LinkToResults,
186
188
  ItemLink:
187
189
  config.ConsultationListItemUrl +
188
190
  itemLinkOperator +
189
191
  'FilterField1=ID&FilterValue1=' +
190
- consultation.fields.id,
192
+ fields.id,
191
193
  };
192
194
  });
193
195
  } catch (err) {
@@ -217,7 +219,8 @@ export async function getMeetings(fromDate, country, userInfo) {
217
219
 
218
220
  return await Promise.all(
219
221
  meetings.map(async (meeting) => {
220
- const meetingId = meeting.fields.id,
222
+ const fields = meeting.fields,
223
+ meetingId = fields.id,
221
224
  participants = allParticipants.filter((p) => p.MeetingId == meetingId),
222
225
  participantsCount = participants.filter((p) => {
223
226
  return p.Participated;
@@ -227,16 +230,17 @@ export async function getMeetings(fromDate, country, userInfo) {
227
230
  }).length;
228
231
 
229
232
  const currentDate = new Date(),
230
- meetingStart = new Date(meeting.fields.Meetingstart),
231
- meetingEnd = new Date(meeting.fields.Meetingend),
232
- meetingTitle = meeting.fields.Title,
233
+ meetingStart = new Date(fields.Meetingstart),
234
+ meetingEnd = new Date(fields.Meetingend),
235
+ meetingTitle = fields.Title,
233
236
  isPast = meetingEnd < currentDate;
234
237
 
235
238
  const countryFilterSuffix = country
236
239
  ? '&FilterField3=Countries&FilterValue3=' + country
237
240
  : '';
238
241
  const meetingFilterSuffix =
239
- '?FilterField1=Meetingtitle&FilterType1=Lookup&FilterValue1=' + meetingTitle;
242
+ '?FilterField1=Meetingtitle&FilterType1=Lookup&FilterValue1=' +
243
+ encodeURIComponent(meetingTitle);
240
244
 
241
245
  let currentParticipant =
242
246
  participants && participants.length && participants.find((p) => p.Email == userInfo.mail);
@@ -253,24 +257,24 @@ export async function getMeetings(fromDate, country, userInfo) {
253
257
  id: meetingId,
254
258
 
255
259
  Title: meetingTitle,
256
- MeetingLink: meeting.fields.MeetingLink,
257
- MeetingRegistrationLink: meeting.fields.MeetingRegistrationLink,
258
- Group: meeting.fields.Group,
260
+ MeetingLink: fields.MeetingLink,
261
+ MeetingRegistrationLink: fields.MeetingRegistrationLink,
262
+ Group: fields.Group,
259
263
 
260
- MeetingStart: new Date(meeting.fields.Meetingstart),
261
- MeetingEnd: new Date(meeting.fields.Meetingend),
262
- MeetingType: meeting.fields.MeetingType,
264
+ MeetingStart: new Date(fields.Meetingstart),
265
+ MeetingEnd: new Date(fields.Meetingend),
266
+ MeetingType: fields.MeetingType,
263
267
 
264
- Year: meetingStart.getFullYear(),
265
- Linktofolder: meeting.fields.Linktofolder,
268
+ Year: parseInt(fields.Year.replace(',', '')),
269
+ Linktofolder: fields.Linktofolder,
266
270
 
267
- NoOfParticipants: country ? participantsCount : meeting.fields.NoOfParticipants,
271
+ NoOfParticipants: country ? participantsCount : fields.NoOfParticipants,
268
272
  ParticipantsUrl:
269
273
  config.MeetingParticipantsListUrl +
270
274
  meetingFilterSuffix +
271
275
  '&FilterField2=Participated&FilterValue2=1&FilterType2=Boolean' +
272
276
  countryFilterSuffix,
273
- NoOfRegistered: country ? registerCount : meeting.fields.NoOfRegistered,
277
+ NoOfRegistered: country ? registerCount : fields.NoOfRegistered,
274
278
  RegisteredUrl:
275
279
  config.MeetingParticipantsListUrl +
276
280
  meetingFilterSuffix +
@@ -282,10 +286,10 @@ export async function getMeetings(fromDate, country, userInfo) {
282
286
  IsUpcoming: meetingStart > new Date(),
283
287
  IsPast: isPast,
284
288
 
285
- IsOnline: meeting.fields.MeetingType && meeting.fields.MeetingType == 'Online',
286
- IsOffline: meeting.fields.MeetingType && meeting.fields.MeetingType != 'Online',
289
+ IsOnline: fields.MeetingType && fields.MeetingType == 'Online',
290
+ IsOffline: fields.MeetingType && fields.MeetingType != 'Online',
287
291
 
288
- CustomMeetingRequest: meeting.fields.CustomMeetingRequests,
292
+ CustomMeetingRequest: fields.CustomMeetingRequests,
289
293
 
290
294
  HasRegistered: !!currentParticipant?.Registered,
291
295
  HasVoted: !!currentParticipant?.Voted,
@@ -477,7 +481,7 @@ export async function getPublications() {
477
481
  ExtraCommsProducts: p.fields.Extra_x0020_comms_x0020_products,
478
482
  Status: p.fields.Status,
479
483
  Date: publicationDate,
480
- Link: p.fields.Link,
484
+ Link: p.fields.Link?.Url,
481
485
  IsPast: publicationDate < currentDate,
482
486
  });
483
487
  });
@@ -767,7 +771,7 @@ export async function getADUser(lookupId) {
767
771
  const adResponse = await apiGet(
768
772
  "/users/?$filter=mail eq '" + userInfo.EMail?.replace("'", "''") + "'",
769
773
  );
770
- return adResponse?.graphClientMessage?.value.length
774
+ return adResponse?.graphClientMessage?.value?.length
771
775
  ? adResponse?.graphClientMessage?.value[0]
772
776
  : undefined;
773
777
  }
@@ -788,7 +792,7 @@ async function loadRating(eventId) {
788
792
  ratingGraphURL + '?$expand=fields&$filter=fields/EventLookupId eq ' + eventId,
789
793
  );
790
794
 
791
- if (response.graphClientMessage?.value.length) {
795
+ if (response.graphClientMessage?.value?.length) {
792
796
  return response.graphClientMessage.value[0];
793
797
  }
794
798
  return undefined;