From 5ec3f5534ca9cf32ac36547c18b28694ff975c99 Mon Sep 17 00:00:00 2001 From: iaigner Date: Thu, 12 Jun 2025 11:57:01 +0200 Subject: [PATCH 01/31] #560: add warning for broken observation links on intervention actions --- src/components/InterventionsList.vue | 87 ++++++++++++++++++- .../shared/ObservationPropertyInput.vue | 48 +++++++++- src/components/shared/MoreTable.vue | 40 ++++++++- src/i18n/de.json | 3 +- src/i18n/en.json | 3 +- 5 files changed, 173 insertions(+), 8 deletions(-) diff --git a/src/components/InterventionsList.vue b/src/components/InterventionsList.vue index c053712e..1302c791 100644 --- a/src/components/InterventionsList.vue +++ b/src/components/InterventionsList.vue @@ -5,7 +5,7 @@ Oesterreichische Vereinigung zur Foerderung der wissenschaftlichen Forschung). Licensed under the Elastic License 2.0. */ + + diff --git a/src/components/shared/MoreTable.vue b/src/components/shared/MoreTable.vue index 0f2f8cce..91bb861b 100644 --- a/src/components/shared/MoreTable.vue +++ b/src/components/shared/MoreTable.vue @@ -33,6 +33,7 @@ Licensed under the Elastic License 2.0. */ import { ComponentFactory, StudyRole, StudyStatus, Visibility } from '@gs'; import { shortenText } from '../../utils/commonUtils'; import { useGlobalStore } from '../../stores/globalStore'; + import ExclamationIcon from './ExclamationIcon.vue'; import { ACTION_ID_QR_CODE } from '../../constants'; @@ -465,35 +466,8 @@ Licensed under the Elastic License 2.0. */
- - - - - - - - - - - + + {{ data[field]}} @@ -744,7 +718,7 @@ Licensed under the Elastic License 2.0. */ padding: 5px; } - .title-has-warning, .title-has-warning svg { + .title-has-warning, .title-has-warning #exclamationIcon { height: 18px; width: auto; } diff --git a/src/components/subComponents/CronScheduleInfo.vue b/src/components/subComponents/CronScheduleInfo.vue index e055c750..75d9c877 100644 --- a/src/components/subComponents/CronScheduleInfo.vue +++ b/src/components/subComponents/CronScheduleInfo.vue @@ -5,6 +5,7 @@ Oesterreichische Vereinigung zur Foerderung der wissenschaftlichen Forschung). Licensed under the Elastic License 2.0. */ + + + + diff --git a/src/composable/useApi.ts b/src/composable/useApi.ts index 4a1dbacf..b501f3ac 100644 --- a/src/composable/useApi.ts +++ b/src/composable/useApi.ts @@ -20,6 +20,7 @@ import { DataApi, ConfigurationApi, CalendarApi, + AuditlogApi } from '@gs'; const apiConfig = { @@ -41,6 +42,7 @@ let calendarApi: CalendarApi; let importExportApi: ImportExportApi; let dataApi: DataApi; let uiConfigApi: ConfigurationApi; +let auditlogApi: AuditlogApi; export function useStudiesApi(): { studiesApi: StudiesApi; @@ -149,3 +151,13 @@ export function useUiConfigApi(): { uiConfigApi, }; } + + +export function useAuditlogApi(): { + auditlogApi: AuditlogApi; +} { + auditlogApi = auditlogApi || new AuditlogApi(apiConfig); + return { + auditlogApi, + }; +} diff --git a/src/i18n/de.json b/src/i18n/de.json index 1b9d4187..6a9a644d 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -61,6 +61,13 @@ "dataDownload": { "description": "Hier können die Studiendaten basierend auf diverse Filter heruntergeladen werden.", "title": "Studiendaten herunterladen" + }, + "auditlogDownload": { + "title": "Auditlog herunterladen", + "description": "Hier kann der aktuelle Auditlog heruntergeladen werden.", + "btnLabel": "Aktuellen Auditlog herunterladen", + "notStartedInfo": "Auditlog-Daten werden erst bei einer aktiven Studie aufgezeichnet.", + "noDataInfo": "Die Studie hat noch keine Auditlog-Daten aufgezeichnet." } }, "global": { @@ -384,7 +391,8 @@ "tabs": { "dataDownload": "Studiendaten herunterladen", "lastDataPoints": "Letzte Datenpunkte", - "recordedObservation": "Erhobene Daten" + "recordedObservation": "Erhobene Daten", + "auditlog": "Auditlog herunterladen" } }, "moreTable": { diff --git a/src/i18n/en.json b/src/i18n/en.json index 235d484c..840b7dea 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -61,6 +61,13 @@ "dataDownload": { "description": "Here you can download the study data based on various filters.", "title": "Download study data" + }, + "auditlogDownload": { + "title": "Download auditlog", + "description": "Here you can download the current auditlog.", + "btnLabel": "Download current auditlog", + "notStartedInfo": "Audit log data is only recorded when a study is active.", + "noDataInfo": "The study has not recorded any audit log data yet." } }, "global": { @@ -384,7 +391,8 @@ "tabs": { "dataDownload": "Export study data", "lastDataPoints": "Latest Data Points", - "recordedObservation": "Recorded Observations" + "recordedObservation": "Recorded Observations", + "auditlog": "Download Auditlog" } }, "moreTable": { diff --git a/src/stores/studyStore.ts b/src/stores/studyStore.ts index d0905e6d..09df6555 100644 --- a/src/stores/studyStore.ts +++ b/src/stores/studyStore.ts @@ -8,8 +8,8 @@ */ import { computed, ComputedRef, ref, Ref } from 'vue'; import { defineStore } from 'pinia'; -import { Study, StudyRole, StudyStatus } from '@gs'; -import { useImportExportApi, useStudiesApi } from '../composable/useApi'; +import { AuditlogMetadata, Study, StudyRole, StudyStatus } from '@gs'; +import { useAuditlogApi, useImportExportApi, useStudiesApi } from '../composable/useApi'; import { AxiosError, AxiosResponse } from 'axios'; import { useErrorHandling } from '../composable/useErrorHandling'; import { useStudyGroupStore } from './studyGroupStore'; @@ -19,12 +19,14 @@ import { useToastService } from '../composable/toastService'; export const useStudyStore = defineStore('study', () => { const { studiesApi } = useStudiesApi(); const { importExportApi } = useImportExportApi(); + const { auditlogApi } = useAuditlogApi(); const { handleIndividualError } = useErrorHandling(); const studyGroupStore = useStudyGroupStore(); const { handleToastErrors } = useToastService(); // State const study: Ref = ref({}); const studies: Ref = ref([]); + const auditlogMetadata: Ref = ref(); // Actions async function getStudy(studyId: number): Promise { @@ -170,6 +172,19 @@ export const useStudyStore = defineStore('study', () => { }); } + async function exportCurrentAuditlog(studyId: number): Promise { + await auditlogApi.exportLastAuditlog(studyId) + .then((rs) => { + window.open(rs.headers.location); + }) + .catch((e: AxiosError) => { + handleIndividualError( + e, + 'cannot generate download token to export study data', + ); + }); + } + function downloadJSON(filename: string, file: File): void { const fileJSON = JSON.stringify(file); const link = document.createElement('a'); @@ -186,6 +201,13 @@ export const useStudyStore = defineStore('study', () => { } } + async function getAuditlogMetadata(studyId: number): Promise { + auditlogMetadata.value = await auditlogApi.getAuditlogMetadata(studyId) + .then((response: AxiosResponse) => response.data) + + return auditlogMetadata.value; + } + // Getters const studyUserRoles: ComputedRef> = computed(() => [ ...(study.value.userRoles || []), @@ -211,5 +233,8 @@ export const useStudyStore = defineStore('study', () => { studyUserRoles, studyStatus, studyId, + auditlogMetadata, + getAuditlogMetadata, + exportCurrentAuditlog }; }); diff --git a/src/views/MonitoringData.vue b/src/views/MonitoringData.vue index 87a6219b..7e117247 100644 --- a/src/views/MonitoringData.vue +++ b/src/views/MonitoringData.vue @@ -13,12 +13,16 @@ Licensed under the Elastic License 2.0. */ import TabPanel from 'primevue/tabpanel'; import DatapointList from '../components/subComponents/DatapointList.vue'; import ParticipationDataList from '../components/ParticipationDataList.vue'; + import AuditlogDownload from '../components/subComponents/AuditlogDownload.vue'; + import { ref } from 'vue'; const studyStore = useStudyStore(); const accessRoles: StudyRole[] = [ StudyRole.StudyAdmin, StudyRole.StudyViewer, ]; + + const activeIndex = ref(0)