feat(modals): replace datepicker and time fields with employee timeblock component for improved time management

This commit is contained in:
Matej Glasnák committed 2026-01-14 16:54:26 +01:00
1 parent fdb4452447
commit 1ce56c544c
2 files changed
+110 -80

No files matched your search

@@ -46,49 +46,14 @@
variant="solo"
class="modal-input"
/>
<!-- <Z-autocomplete label="Vyhľadať zamestnanca" :value="blocker?.employee ? `${blocker?.employee?.name} ${blocker?.employee?.surname}` : ''" :options="enrichedEmployees.map(employee => ({key: employee.id, value: employee._fullName}))" :clear-on-submit="false" :class="{ error: v$.blocker.employee.$errors.length }" class="modal-input" @option-selected="onEmployeeSelected" /> -->
<Z-datepicker
v-model="datepicker"
class="modal-input mb-5"
:auto-apply="true"
format="dd.MM.yyyy"
locale="sk"
:class="{ error: v$.datepicker.$errors?.length }"
:validations="v$.datepicker"
select-text="Vybrať"
cancel-text="Zrušiť"
placeholder="Dátum"
label="Dátum"
:enable-time-picker="false"
/>
<v-text-field
v-model="starts_at_time"
type="time"
variant="solo"
bg-color="white"
:hint="
v$.starts_at_time.$errors
? errorUtils.parseErrors(v$.starts_at_time.$errors)
: null
"
:class="{ error: v$.starts_at_time.$errors.length }"
class="mb-1 modal-input"
label="Začiatok"
/>
<v-text-field
v-model="ends_at_time"
type="time"
variant="solo"
bg-color="white"
:hint="
v$.ends_at_time.$errors
? errorUtils.parseErrors(v$.ends_at_time.$errors)
: null
"
:class="{ error: v$.ends_at_time.$errors.length }"
class="modal-input"
label="Koniec"
<!-- Timeblock Component -->
<a-employee-timeblock
ref="employeeTimeblockRef"
v-model="timeblockData"
:employee="selectedEmployee"
:deletable="false"
:index="0"
/>
</v-card-actions>
@@ -107,7 +72,6 @@
</template>
<script>
import ZDatepicker from '@/plugins/tm@_components/z-datepicker.vue';
import useVuelidate from '@vuelidate/core';
import { required } from '@vuelidate/validators';
import errorUtils from '@/plugins/lib@auth/_utils/error.utils';
@@ -118,14 +82,14 @@ import modalMixin from '@/plugins/tm@modal/_mixins/modal.mixin';
import AResizeHandle from '@/plugins/tm@modal/_components/a-resize-handle.vue';
import AResetSizeButton from '@/plugins/tm@modal/_components/a-reset-size-button.vue';
import ACloseButton from '@/plugins/tm@modal/_components/a-close-button.vue';
import AEmployeeTimeblock from '@/plugins/tm@modal/task/_components/employees/a-employee-timeblock.vue';
export default {
components: {
ZDatepicker,
// ZAutocomplete
AResizeHandle,
AResetSizeButton,
ACloseButton,
AEmployeeTimeblock,
},
mixins: [AutocompleteMixin, modalMixin],
@@ -137,9 +101,13 @@ export default {
v$: useVuelidate(),
errorUtils,
blocker: null,
datepicker: null,
starts_at_time: null,
ends_at_time: null,
timeblockData: {
id: null,
date: new Date(),
starts_at_time: null,
ends_at_time: null,
employee_id: null,
},
employee: null,
modalName: 'z-blocker-modal',
modalSize: 'large',
@@ -150,6 +118,13 @@ export default {
...mapGetters('tm', ['enrichedEmployees']),
...mapGetters('auth', ['user']),
selectedEmployee() {
if (!this.blocker?.employee_id) return null;
return this.enrichedEmployees.find(
(emp) => emp.id === this.blocker.employee_id,
);
},
},
validations() {
@@ -158,25 +133,27 @@ export default {
name: { required },
start: { required },
end: { required },
// employee: { required }
},
starts_at_time: { required },
ends_at_time: { required },
datepicker: { required },
};
},
watch: {
ends_at_time() {
'blocker.employee_id'(newEmployeeId) {
// Update timeblock employee_id when blocker employee changes
if (this.timeblockData) {
this.timeblockData.employee_id = newEmployeeId;
}
},
'timeblockData.date'() {
this._syncDatesTimes();
},
starts_at_time() {
'timeblockData.starts_at_time'() {
this._syncDatesTimes();
},
datepicker() {
'timeblockData.ends_at_time'() {
this._syncDatesTimes();
},
},
@@ -185,9 +162,13 @@ export default {
async showModal({ blocker }) {
this.visible = true;
this.blocker = null;
this.datepicker = null;
this.starts_at_time = null;
this.ends_at_time = null;
this.timeblockData = {
id: null,
date: new Date(),
starts_at_time: null,
ends_at_time: null,
employee_id: null,
};
this.$nextTick(() => {
this.v$.$reset();
@@ -199,11 +180,18 @@ export default {
`v1/blockers/${blocker.id}`,
);
this.starts_at_time = moment(blocker.start).format('HH:mm');
this.ends_at_time = moment(blocker.end).format('HH:mm');
this.datepicker = new Date(blocker.start);
const startMoment = moment(blocker.start);
const endMoment = moment(blocker.end);
this.timeblockData = {
id: null,
date: startMoment.toDate(),
starts_at_time: startMoment.format('HH:mm'),
ends_at_time: endMoment.format('HH:mm'),
employee_id: blocker.employee.id,
};
blocker.employee_id = blocker.employee.id;
//fixnut validacie employee_id
this.blocker = blocker;
} else {
this.blocker = { start: null, employee_id: this.user.id };
@@ -213,9 +201,16 @@ export default {
blocker.datepicker &&
blocker.employee_id
) {
this.datepicker = blocker.datepicker;
this.starts_at_time = blocker.starts_at_time;
this.timeblockData = {
id: null,
date: blocker.datepicker,
starts_at_time: blocker.starts_at_time,
ends_at_time: blocker.ends_at_time,
employee_id: blocker.employee_id,
};
this.blocker.employee_id = blocker.employee_id;
} else {
this.timeblockData.employee_id = this.user.id;
}
}
} catch (error) {
@@ -224,11 +219,13 @@ export default {
},
async save() {
if (!(await this.v$.$validate())) return;
// Validate using the employee timeblock component's validation
const isTimeblockValid =
await this.$refs.employeeTimeblockRef?.validate();
if (!isTimeblockValid || !(await this.v$.$validate())) return;
try {
// this.blocker.employee_id = this.blocker.employee?.id
if (this.blocker.id) {
const { blocker } = await this.$axios.put_auth(
`v1/blockers/${this.blocker.id}`,
@@ -253,29 +250,34 @@ export default {
closeModal() {
this.visible = false;
this.timeblockData = {
id: null,
date: new Date(),
starts_at_time: null,
ends_at_time: null,
employee_id: null,
};
this.$emit('close', this.blocker);
},
onEmployeeSelected({ key }) {
this.blocker.employee = this.enrichedEmployees.find(
(employee) => employee.id == key,
);
},
_syncDatesTimes() {
if (!this.starts_at_time || !this.ends_at_time || !this.datepicker)
if (
!this.timeblockData.starts_at_time ||
!this.timeblockData.ends_at_time ||
!this.timeblockData.date
)
return;
const splittedStart = this.starts_at_time.split(':');
const splittedEnd = this.ends_at_time.split(':');
const splittedStart = this.timeblockData.starts_at_time.split(':');
const splittedEnd = this.timeblockData.ends_at_time.split(':');
this.blocker.start = moment(this.datepicker)
this.blocker.start = moment(this.timeblockData.date)
.set({
hours: parseInt(splittedStart[0]),
minutes: parseInt(splittedStart[1]),
})
.format('YYYY-MM-DD HH:mm');
this.blocker.end = moment(this.datepicker)
this.blocker.end = moment(this.timeblockData.date)
.set({
hours: parseInt(splittedEnd[0]),
minutes: parseInt(splittedEnd[1]),
@@ -340,3 +342,13 @@ function oo_cm() {
return v;
} /*eslint unicorn/no-abusive-eslint-disable:,eslint-comments/disable-enable-pair:,eslint-comments/no-unlimited-disable:,eslint-comments/no-aggregating-enable:,eslint-comments/no-duplicate-disable:,eslint-comments/no-unused-disable:,eslint-comments/no-unused-enable:,*/
</script>
<style lang="sass" scoped>
::v-deep
// Hide employee select and add/remove buttons in timeblock component
.timeblock__header__top
display: none
.actions
display: none
</style>
+19 -1
View File
@@ -832,8 +832,16 @@ export default {
}
if (!data.deadline) {
this.subtask = this._enrichTask(data);
this.editMode = editMode || !data.id;
return (this.subtask = this._enrichTask(data));
// Set Quill editor content after subtask is loaded and editMode is set
this.$nextTick(() => {
if (this.$refs['description']) {
this.$refs['description'].setHTML(this.subtask.description || '');
}
});
return;
}
const splittedDeadline = data.deadline.split(' ');
@@ -963,6 +971,16 @@ export default {
}
}
this.editMode = editMode || !this.subtask.id;
// Set Quill editor content after editMode is set
// This ensures the description is loaded when opening in edit mode
if (this.editMode) {
this.$nextTick(() => {
if (this.$refs['description']) {
this.$refs['description'].setHTML(this.subtask?.description || '');
}
});
}
} catch (error) {
this.$toast.error(this.$parseError(error));
}