mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 14:29:34 +00:00
feature(task-modal): added ability to edit timeblocks both on create and edit
This commit is contained in:
1 parent
80d0e57351
commit
056b27b016
2 files changed
+119
-71
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
|
|
||||||
@@ -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 timeblocks"
|
v-for="(timeblock, index) in editableTimeblocks"
|
||||||
: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
|
:editable="true"
|
||||||
: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">
|
<div class="end-button w-100">
|
||||||
<button
|
<button @click="addTimeblock">Pridať časový blok</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">
|
|
||||||
<v-text-field
|
|
||||||
: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) {
|
||||||
|
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;
|
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) {
|
||||||
|
if (this.subtask?.id) {
|
||||||
|
// Update mode
|
||||||
|
this.subtask.timeblocks.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
// Create mode
|
||||||
this.timeblocks.splice(index, 1);
|
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) {
|
||||||
|
|||||||
Reference in new issue
Block a user