From da3f176bb2c956907c4ddbe6a5f600dbdf9cd400 Mon Sep 17 00:00:00 2001 From: "Blazej M. Baczkowski" Date: Sun, 26 Apr 2026 20:26:10 +0200 Subject: [PATCH] feat: allow to edit / clear data shared across study, session, participant - add JSON editor dialog component - integrate editor in study info, study participants, and participants - allow editing, clearing, saving only valid JSON input - adds migration schema for sessions - modifies session controller for error handling and validation Relates to #219 --- app/Controllers/Http/SessionController.js | 24 +- ...426202700_make_session_columns_nullable.js | 23 ++ resources/assets/js/endpoints.js | 1 + .../ParticipantViewData.vue | 6 + .../ParticipantsList/ParticipantsList.vue | 1 + .../ParticipantsPanel/ParticipantsPanel.vue | 1 + .../StudyParticipantsList.vue | 18 ++ .../Studies/page/StudyInfo/StudyInfo.vue | 6 + .../JsonEditorDialog/JsonEditorDialog.vue | 271 ++++++++++++++++++ resources/pages/dashboard/participants.vue | 27 +- .../pages/dashboard/studies/_id/index.vue | 31 +- 11 files changed, 400 insertions(+), 9 deletions(-) create mode 100644 database/migrations/20260426202700_make_session_columns_nullable.js create mode 100644 resources/components/common/JsonEditorDialog/JsonEditorDialog.vue diff --git a/app/Controllers/Http/SessionController.js b/app/Controllers/Http/SessionController.js index 25c35b78..ee4d3c5e 100644 --- a/app/Controllers/Http/SessionController.js +++ b/app/Controllers/Http/SessionController.js @@ -57,7 +57,10 @@ class SessionController { const result = await Session.query() .where('study_id', studyID) .where('participant_id', participantID) - .firstOrFail() + .first() + if (!result) { + return response.status(404).json({ message: 'Cannot find database row for Session model' }) + } return response.json({ data: result }) } @@ -110,16 +113,23 @@ class SessionController { * @param {Response} ctx.response */ async update ({ request, response }) { - const validation = await validate(request.all(), { - study_id: 'integer', - participant_id: 'string', + const rules = { data: 'required|json' - }) - if (validation.fails()) { - return response.status(422).json(validation.messages()) } const studyID = request.input('study_id', null) const participantID = request.input('participant_id', null) + + if (studyID !== null) { + rules.study_id = 'integer' + } + if (participantID !== null) { + rules.participant_id = 'string' + } + + const validation = await validate(request.all(), rules) + if (validation.fails()) { + return response.status(422).json(validation.messages()) + } const data = request.input('data', {}) const record = await Session.query() .where('study_id', studyID) diff --git a/database/migrations/20260426202700_make_session_columns_nullable.js b/database/migrations/20260426202700_make_session_columns_nullable.js new file mode 100644 index 00000000..76b3b218 --- /dev/null +++ b/database/migrations/20260426202700_make_session_columns_nullable.js @@ -0,0 +1,23 @@ +'use strict' + +/** @type {import('@adonisjs/lucid/src/Schema')} */ +const Schema = use('Schema') + +class MakeSessionColumnsNullable extends Schema { + up () { + this.alter('sessions', (table) => { + table.integer('study_id').unsigned().nullable().alter() + table.string('participant_id').nullable().alter() + }) + } + + down () { + // Keep nullable since the application requires it + this.alter('sessions', (table) => { + table.integer('study_id').unsigned().nullable().alter() + table.string('participant_id').nullable().alter() + }) + } +} + +module.exports = MakeSessionColumnsNullable \ No newline at end of file diff --git a/resources/assets/js/endpoints.js b/resources/assets/js/endpoints.js index 65168650..b1080bb1 100644 --- a/resources/assets/js/endpoints.js +++ b/resources/assets/js/endpoints.js @@ -7,6 +7,7 @@ export const STUDIES = `${API_PREFIX}/studies` export const PARTICIPANTS = `${API_PREFIX}/participants` export const PARTICIPATIONS = `${API_PREFIX}/participations` export const JOBS = `${API_PREFIX}/jobs` +export const SESSIONS = `${API_PREFIX}/sessions` // Misc routes export const RECOVER_PASSWORD = `${AUTH_PREFIX}/password/recover` diff --git a/resources/components/Participants/ParticipantViewData/ParticipantViewData.vue b/resources/components/Participants/ParticipantViewData/ParticipantViewData.vue index ac7d2536..c97b7761 100644 --- a/resources/components/Participants/ParticipantViewData/ParticipantViewData.vue +++ b/resources/components/Participants/ParticipantViewData/ParticipantViewData.vue @@ -33,6 +33,12 @@ + + + mdi-database + + Edit Participant Data +