feature(task-modal): added ability to edit timeblocks both on create and edit

This commit is contained in:
Matej Glasnák committed 2025-10-14 01:02:36 +02:00
1 parent 80d0e57351
commit 056b27b016
2 files changed
+133 -85

No files matched your search

@@ -27,7 +27,6 @@
<v-text-field <v-text-field
:value="timeblock.starts_at_time" :value="timeblock.starts_at_time"
@input="updateStartTime" @input="updateStartTime"
@focus="onInputFocus"
:class="{ error: v$?.timeblock.starts_at_time.$errors.length }" :class="{ error: v$?.timeblock.starts_at_time.$errors.length }"
type="time" type="time"
variant="solo" variant="solo"
@@ -37,7 +36,6 @@
<v-text-field <v-text-field
:value="timeblock.ends_at_time" :value="timeblock.ends_at_time"
@input="updateEndTime" @input="updateEndTime"
@focus="onInputFocus"
:class="{ error: v$?.timeblock.ends_at_time.$errors.length }" :class="{ error: v$?.timeblock.ends_at_time.$errors.length }"
type="time" type="time"
variant="solo" variant="solo"
@@ -109,20 +107,20 @@ export default {
async openDatepicker(ref) { async openDatepicker(ref) {
this.$refs[ref].openMenu(); this.$refs[ref].openMenu();
}, },
updateDate(date) { updateDate(date) {
const timeblock = { ...this.timeblock }; const updated = { ...this.timeblock, date };
if (date) timeblock.date = date; this.$emit('updateTimeblock', updated);
this.$emit('updateTimeblock', timeblock);
}, },
updateStartTime(event) { updateStartTime(event) {
const timeblock = { ...this.timeblock }; const updated = { ...this.timeblock, starts_at_time: event.target.value };
timeblock.starts_at_time = event.target.value; this.$emit('updateTimeblock', updated);
this.$emit('updateTimeblock', timeblock);
}, },
updateEndTime(event) { updateEndTime(event) {
const timeblock = { ...this.timeblock }; const updated = { ...this.timeblock, ends_at_time: event.target.value };
timeblock.ends_at_time = event.target.value; this.$emit('updateTimeblock', updated);
this.$emit('updateTimeblock', timeblock);
}, },
}, },
}; };
@@ -158,4 +156,3 @@ export default {
width: 100% !important width: 100% !important
pointer-events: none pointer-events: none
</style> </style>
e
+124 -73
View File
@@ -234,70 +234,22 @@
</div> </div>
</template> </template>
<template v-if="!subtask.id"> <div class="timeblocks w-100">
<div class="timeblocks w-100"> <A-timeblock
<A-timeblock v-for="(timeblock, index) in editableTimeblocks"
v-for="(timeblock, index) in timeblocks" :key="index"
:key="index" @updateTimeblock="(val) => updateTimeblock(val, index)"
@updateTimeblock="(val) => updateTimeblock(val, index)" @removeTimeblock="removeTimeblock(index)"
@removeTimeblock="removeTimeblock(index)" :timeblock="timeblock"
:timeblock="timeblock" :deletable="index > 0"
:deletable="index > 0" :editable="true"
editable :ref="`timetable-${index}`"
:ref="`timetable-${index}`" :prevent-input-focus="preventInputFocus"
:prevent-input-focus="preventInputFocus" :on-input-focus="onInputFocus"
:on-input-focus="onInputFocus" :on-date-open="onDateOpen"
:on-date-open="onDateOpen"
/>
<div class="end-button w-100">
<button
@click="
timeblocks.push({
date: null,
starts_at_time: null,
ends_at_time: null,
})
"
>
Pridať časový blok
</button>
</div>
</div>
</template>
<div
v-else
v-for="(timeblock, index) in subtask.timeblocks"
:key="index"
class="timeblock w-100"
>
<Datepicker
:model-value="timeblock.start.substring(0, 10)"
format="dd.MM.yyyy"
locale="sk"
select-text="Vybrať"
cancel-text="Zrušiť"
placeholder="Dátum"
:auto-apply="true"
:enable-time-picker="false"
readonly
class="mb-5 modal-input"
/> />
<div class="splitted-time-inputs w-100"> <div class="end-button w-100">
<v-text-field <button @click="addTimeblock">Pridať časový blok</button>
:value="timeblock.start.substring(11, 16)"
type="time"
variant="solo"
bg-color="white"
label="Začiatok"
/>
<v-text-field
:value="timeblock.end.substring(11, 16)"
type="time"
variant="solo"
bg-color="white"
label="Koniec"
/>
</div> </div>
</div> </div>
@@ -459,6 +411,27 @@ export default {
_fullName: `${mastertask.name} [${mastertask.project_code}]`, _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() { validations() {
@@ -520,11 +493,42 @@ export default {
}, },
updateTimeblock(val, index) { 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) { 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 = {} }) { async showModal({ subtask, editMode = false, mastertask = {} }) {
@@ -549,6 +553,35 @@ export default {
`v1/subtasks/${subtask.id}`, `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) { if (!data.deadline) {
this.editMode = editMode || !data.id; this.editMode = editMode || !data.id;
return (this.subtask = this._enrichTask(data)); return (this.subtask = this._enrichTask(data));
@@ -624,9 +657,7 @@ export default {
async validateAllFields() { async validateAllFields() {
const employeesValid = await this._validateEmployees(); const employeesValid = await this._validateEmployees();
const timeblocksValid = this.subtask?.id const timeblocksValid = await this._validateTimeblocks();
? true
: await this._validateTimeblocks();
if (!(await this.v$.$validate())) return; if (!(await this.v$.$validate())) return;
if (!employeesValid || !timeblocksValid) return; if (!employeesValid || !timeblocksValid) return;
@@ -661,9 +692,22 @@ export default {
return timesheet; 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( const { subtask } = await this.$axios.patch_auth_form(
`v1/subtasks/${this.subtask.id}`, `v1/subtasks/${this.subtask.id}`,
{ ...this.subtask, timesheets }, { ...this.subtask, timesheets, timeblocks },
); );
localStorage.removeItem('subtaskData'); localStorage.removeItem('subtaskData');
@@ -809,12 +853,19 @@ export default {
}, },
async _validateTimeblocks() { async _validateTimeblocks() {
let invalid = false; let valid = true;
for (let i = 0; i < this.timeblocks.length; i++) { const timeblocks = this.editableTimeblocks;
for (let i = 0; i < timeblocks.length; i++) {
const ref = this.$refs[`timetable-${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) { _enrichTask(subtask) {