From 056b27b016e839879d0f85b263d905b7de39915d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Glasn=C3=A1k?= Date: Tue, 14 Oct 2025 01:02:36 +0200 Subject: [PATCH] feature(task-modal): added ability to edit timeblocks both on create and edit --- .../tm@modal/task/_components/a-timeblock.vue | 21 +- .../plugins/tm@modal/task/z-task-modal.vue | 197 +++++++++++------- 2 files changed, 133 insertions(+), 85 deletions(-) diff --git a/app/src/plugins/tm@modal/task/_components/a-timeblock.vue b/app/src/plugins/tm@modal/task/_components/a-timeblock.vue index fe53717..6ad2e14 100644 --- a/app/src/plugins/tm@modal/task/_components/a-timeblock.vue +++ b/app/src/plugins/tm@modal/task/_components/a-timeblock.vue @@ -27,7 +27,6 @@ -e diff --git a/app/src/plugins/tm@modal/task/z-task-modal.vue b/app/src/plugins/tm@modal/task/z-task-modal.vue index 0b8e247..8ac4b3a 100644 --- a/app/src/plugins/tm@modal/task/z-task-modal.vue +++ b/app/src/plugins/tm@modal/task/z-task-modal.vue @@ -234,70 +234,22 @@ - - -
- + -
- - +
+
@@ -459,6 +411,27 @@ export default { _fullName: `${mastertask.name} [${mastertask.project_code}]`, })); }, + + // Unified timeblocks for both create and update modes + editableTimeblocks: { + get() { + if (this.subtask?.id) { + // Update mode: use subtask.timeblocks (from BE) + return this.subtask.timeblocks || []; + } + // Create mode: use this.timeblocks (local state) + return this.timeblocks; + }, + set(value) { + if (this.subtask?.id) { + // Update mode: update subtask.timeblocks (Vue 3 reactivity) + this.subtask.timeblocks = value; + } else { + // Create mode: update this.timeblocks + this.timeblocks = value; + } + }, + }, }, validations() { @@ -520,11 +493,42 @@ export default { }, updateTimeblock(val, index) { - this.timeblocks[index] = val; + if (this.subtask?.id) { + // Update mode: direct assignment (Vue 3 reactivity) + this.subtask.timeblocks[index] = val; + } else { + // Create mode: direct assignment + this.timeblocks[index] = val; + } + }, + + addTimeblock() { + const newTimeblock = { + date: null, + starts_at_time: null, + ends_at_time: null, + }; + + if (this.subtask?.id) { + // Update mode + if (!this.subtask.timeblocks) { + this.subtask.timeblocks = []; + } + this.subtask.timeblocks.push(newTimeblock); + } else { + // Create mode + this.timeblocks.push(newTimeblock); + } }, removeTimeblock(index) { - this.timeblocks.splice(index, 1); + if (this.subtask?.id) { + // Update mode + this.subtask.timeblocks.splice(index, 1); + } else { + // Create mode + this.timeblocks.splice(index, 1); + } }, async showModal({ subtask, editMode = false, mastertask = {} }) { @@ -549,6 +553,35 @@ export default { `v1/subtasks/${subtask.id}`, ); + // Convert BE format timeblocks to internal format + const convertedTimeblocks = []; + if (data.timeblocks && data.timeblocks.length > 0) { + data.timeblocks.forEach((timeblock) => { + // If already in internal format, keep as is + if (timeblock.date) { + convertedTimeblocks.push(timeblock); + return; + } + + // Convert from BE format to internal format + if (timeblock.start && timeblock.end) { + const startMoment = moment( + timeblock.start, + 'YYYY-MM-DD HH:mm:ss', + ); + const endMoment = moment(timeblock.end, 'YYYY-MM-DD HH:mm:ss'); + convertedTimeblocks.push({ + id: timeblock.id || null, + date: startMoment.toDate(), + starts_at_time: startMoment.format('HH:mm'), + ends_at_time: endMoment.format('HH:mm'), + is_finished: timeblock.is_finished || 0, + }); + } + }); + } + data.timeblocks = convertedTimeblocks; + if (!data.deadline) { this.editMode = editMode || !data.id; return (this.subtask = this._enrichTask(data)); @@ -624,9 +657,7 @@ export default { async validateAllFields() { const employeesValid = await this._validateEmployees(); - const timeblocksValid = this.subtask?.id - ? true - : await this._validateTimeblocks(); + const timeblocksValid = await this._validateTimeblocks(); if (!(await this.v$.$validate())) return; if (!employeesValid || !timeblocksValid) return; @@ -661,9 +692,22 @@ export default { return timesheet; }); + // Convert timeblocks from internal format to BE format for update + const timeblocks = (this.subtask.timeblocks || []) + .filter((timeblock) => timeblock.date) // Only send timeblocks with dates + .map((timeblock) => { + const dateStr = moment(timeblock.date).format('YYYY-MM-DD'); + return { + id: timeblock.id || null, + start: `${dateStr} ${timeblock.starts_at_time}:00`, + end: `${dateStr} ${timeblock.ends_at_time}:00`, + is_finished: timeblock.is_finished || 0, + }; + }); + const { subtask } = await this.$axios.patch_auth_form( `v1/subtasks/${this.subtask.id}`, - { ...this.subtask, timesheets }, + { ...this.subtask, timesheets, timeblocks }, ); localStorage.removeItem('subtaskData'); @@ -809,12 +853,19 @@ export default { }, async _validateTimeblocks() { - let invalid = false; - for (let i = 0; i < this.timeblocks.length; i++) { + let valid = true; + const timeblocks = this.editableTimeblocks; + + for (let i = 0; i < timeblocks.length; i++) { const ref = this.$refs[`timetable-${i}`]; - invalid = (await ref[0].v$.$validate()) || invalid; + if (ref && ref[0]) { + const isValid = await ref[0].v$.$validate(); + if (!isValid) { + valid = false; + } + } } - return invalid; + return valid; }, _enrichTask(subtask) {