diff --git a/docs/gherkin/Relative-Scheduler-time-and-participant-starting-time.md b/docs/gherkin/Relative-Scheduler-time-and-participant-starting-time.md new file mode 100644 index 00000000..2e4669fd --- /dev/null +++ b/docs/gherkin/Relative-Scheduler-time-and-participant-starting-time.md @@ -0,0 +1,11 @@ +# Gherkin Test: Bug fix relative scheduler (UTC time) and participant starting time + +### Background: +- Study was created or exists and has a valid relative observation and 10 participants. + +| **Scenario** | **Given** | **Steps** | **Expected Result** | **Result** | **Note** | +|---------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|-----------------------------------------------------| +| Participant list exists and updates | Background, and that a smartphone with the more app is prepared | | THEN the participants list should show a "started on" coloumn
AND it should update with the correct starting date once the user data has been entered to the smartphone | as expected | After reload the startdate and time has been shown. | +| Relative scheduer shows the correct start and end times | Background, and the observation tab is open | | THEN the relative scheduler should show the correct start and end date (I have entered) at any given point during this test. | as expected | | +| The exported calendar shows the correct dates. | Background, and the study list is open. | | THEN the existing observation(s) should be displayed correctly
AND should not have a one hour offset compared to the observation(s) in the study manager | as expected | | +| Correct observation display on the smartphone | Background, one observation with a relative calendar exists with an start time of day one at 00:00 and an end time of 12:00
AND it is repeated every day
AND a mobile phone with the MORE app is ready to join the study
AND the participant tab is open | | THEN the observaions should be shown at the correct time, at the correct day. | as expected | | diff --git a/openapi/RelativeEvent.yaml b/openapi/RelativeEvent.yaml index 2e6833f1..10e910fb 100644 --- a/openapi/RelativeEvent.yaml +++ b/openapi/RelativeEvent.yaml @@ -19,6 +19,7 @@ components: - MINUTE - HOUR - DAY + RelativeDate: type: object description: A date relative to a specific base date (e.g study start) @@ -29,6 +30,10 @@ components: type: string format: time description: Follows ISO 8601 format for time + timezone: + type: string + description: Time Zone string for relative event + RelativeRecurrenceRule: type: object description: A recurrence rule relative to dtstart diff --git a/openapi/StudyManagerAPI.yaml b/openapi/StudyManagerAPI.yaml index 40fb4c95..1bce37a3 100644 --- a/openapi/StudyManagerAPI.yaml +++ b/openapi/StudyManagerAPI.yaml @@ -1397,6 +1397,10 @@ components: readOnly: true status: $ref: '#/components/schemas/ParticipantStatus' + start: + type: string + format: date-time + readOnly: true created: type: string format: date-time diff --git a/src/components/ObservationList.vue b/src/components/ObservationList.vue index 81f80ed4..f3043dd4 100644 --- a/src/components/ObservationList.vue +++ b/src/components/ObservationList.vue @@ -36,7 +36,6 @@ Licensed under the Elastic License 2.0. */ import { useErrorHandling } from '../composable/useErrorHandling'; import DeleteMoreTableRowDialog from './dialog/DeleteMoreTableRowDialog.vue'; import dayjs from 'dayjs'; - import { ZTimeStringToOffsetTimeString } from '../utils/dateUtils'; const loader = useLoader(); const { observationsApi } = useObservationsApi(); @@ -314,15 +313,13 @@ Licensed under the Elastic License 2.0. */ schedule.dtstart.offset?.unit ? `${t( `scheduler.preview.unit.${schedule.dtstart.offset.unit}` - )} ${ - schedule.dtstart.offset.value - }, ${ZTimeStringToOffsetTimeString(schedule.dtstart.time)}` + )} ${schedule.dtstart.offset.value}, ${schedule.dtstart.time}` : undefined; case 'dtend': return schedule.dtend.offset?.value && schedule.dtend.offset?.unit ? `${t(`scheduler.preview.unit.${schedule.dtend.offset.unit}`)} ${ schedule.dtend.offset.value - }, ${ZTimeStringToOffsetTimeString(schedule.dtend.time)} ` + }, ${schedule.dtend.time} ` : undefined; default: return undefined; diff --git a/src/components/ParticipantList.vue b/src/components/ParticipantList.vue index 71e42eaf..ebff2ce2 100644 --- a/src/components/ParticipantList.vue +++ b/src/components/ParticipantList.vue @@ -98,6 +98,14 @@ Licensed under the Elastic License 2.0. */ placeholder: t('global.placeholder.noGroup'), columnWidth: '15vw', }, + { + field: 'start', + header: t('participants.props.individualStart'), + type: MoreTableFieldType.datetime, + sortable: true, + placeholder: '-', + columnWidth: '10vw', + }, ]; const rowActions: MoreTableAction[] = [ diff --git a/src/components/shared/MoreTable.vue b/src/components/shared/MoreTable.vue index bd4eac8b..25caf578 100644 --- a/src/components/shared/MoreTable.vue +++ b/src/components/shared/MoreTable.vue @@ -754,7 +754,8 @@ Licensed under the Elastic License 2.0. */ {{ dayjs(data['__internalValue_' + field]).format('DD/MM/YYYY') }} - + - + {{ dayjs(data[field]).format('DD/MM/YYYY, HH:mm') }} diff --git a/src/components/shared/RelativeScheduler.vue b/src/components/shared/RelativeScheduler.vue index 7c4df5af..bf6ab58e 100644 --- a/src/components/shared/RelativeScheduler.vue +++ b/src/components/shared/RelativeScheduler.vue @@ -5,7 +5,6 @@ import InputNumber from 'primevue/inputnumber'; import Dropdown from 'primevue/dropdown'; import Checkbox from 'primevue/checkbox'; - import { ZTimeToOffsetTime } from '../../utils/dateUtils'; import { RelativeEvent, RelativeRecurrenceRule, @@ -28,6 +27,7 @@ unit: schedule.dtstart?.offset?.unit, }, time: schedule.dtstart?.time, + timezone: schedule.dtstart?.timezone, }, dtend: { offset: { @@ -35,6 +35,7 @@ unit: schedule.dtend?.offset?.unit, }, time: schedule.dtend?.time, + timezone: schedule.dtend?.timezone, }, rrrule: { frequency: { @@ -56,7 +57,6 @@ parseInt(schedule.dtstart.time?.substring(0, 2)), parseInt(schedule.dtstart.time?.substring(3, 5), 0) ); - startTime.value = ZTimeToOffsetTime(startTime.value); } else { startTime.value.setHours(10, 30); } @@ -65,14 +65,13 @@ parseInt(schedule.dtend.time?.substring(0, 2)), parseInt(schedule.dtend.time?.substring(3, 5), 0) ); - endTime.value = ZTimeToOffsetTime(endTime.value); } else { endTime.value.setHours(18, 30); } returnSchedule.value.dtstart.time = returnSchedule.value.dtstart.time ? returnSchedule.value.dtstart.time - : '10:10'; + : '10:00'; returnSchedule.value.dtend.time = returnSchedule.value.dtend.time ? returnSchedule.value.dtend.time : '18:00'; @@ -250,15 +249,24 @@ function save() { returnSchedule.value.dtstart.time = startTime.value - ?.toISOString() - .substring(11, 16); + ?.toTimeString() + .substring(0, 5); returnSchedule.value.dtend.time = endTime.value - ?.toISOString() - .substring(11, 16); + ?.toTimeString() + .substring(0, 5); returnSchedule.value.dtstart.offset = rDtstartOffset.value; returnSchedule.value.dtend.offset = rDtendOffset.value; + if (typeof returnSchedule.value.dtstart.time !== 'undefined') { + returnSchedule.value.dtstart.timezone = + Intl.DateTimeFormat().resolvedOptions().timeZone; + } + if (typeof returnSchedule.value.dtend.time !== 'undefined') { + returnSchedule.value.dtend.timezone = + Intl.DateTimeFormat().resolvedOptions().timeZone; + } + if (repeatChecked.value) { const rrrule: RelativeRecurrenceRule = { frequency: rFrequency.value, @@ -317,6 +325,7 @@ :placeholder=" $t('scheduler.dialog.relativeSchedule.placeholder.dtstartOffset') " + :min="1" @blur="calculatedRepeat()" /> @@ -350,6 +359,7 @@ :placeholder=" $t('scheduler.dialog.relativeSchedule.placeholder.dtendOffset') " + :min="1" @blur="calculatedRepeat()" /> diff --git a/src/components/subComponents/SchedulerInfoBlock.vue b/src/components/subComponents/SchedulerInfoBlock.vue index aebca34e..35a1e766 100644 --- a/src/components/subComponents/SchedulerInfoBlock.vue +++ b/src/components/subComponents/SchedulerInfoBlock.vue @@ -14,7 +14,6 @@ Licensed under the Elastic License 2.0. */ import Button from 'primevue/button'; import { useI18n } from 'vue-i18n'; import dayjs from 'dayjs'; - import { ZTimeStringToOffsetTimeString } from '../../utils/dateUtils'; const { t } = useI18n(); @@ -79,16 +78,14 @@ Licensed under the Elastic License 2.0. */ schedule.dtstart.offset?.unit ? `${t( `scheduler.preview.unit.${schedule.dtstart.offset.unit}` - )} ${ - schedule.dtstart.offset.value - }, ${ZTimeStringToOffsetTimeString(schedule.dtstart.time)}` + )} ${schedule.dtstart.offset.value}, ${schedule.dtstart.time}` : undefined; } case 'dtend': return schedule.dtend.offset?.value && schedule.dtend.offset?.unit ? `${t(`scheduler.preview.unit.${schedule.dtend.offset.unit}`)} ${ schedule.dtend.offset.value - }, ${ZTimeStringToOffsetTimeString(schedule.dtend.time)} ` + }, ${schedule.dtend.time} ` : undefined; default: return undefined; diff --git a/src/i18n/de.json b/src/i18n/de.json index 528483d5..7f5693ce 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -325,7 +325,8 @@ "participantId": "Teilnehmer ID", "alias": "Alias", "token": "Token", - "registrationToken": "Registrierungstoken" + "registrationToken": "Registrierungstoken", + "individualStart": "Gestartet am" }, "dialog": { "header": { diff --git a/src/i18n/en.json b/src/i18n/en.json index 7fb26d9d..704c5f45 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -325,7 +325,8 @@ "participantId": "Participant ID", "alias": "Alias", "token": "Token", - "registrationToken": "Registration token" + "registrationToken": "Registration token", + "individualStart": "Started on" }, "dialog": { "header": {