mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 14:29:34 +00:00
wip(task-timeline): stage changes
This commit is contained in:
1 parent
4768986a25
commit
b1771ead05
11 files changed
+2220
-682
No files matched your search
@@ -88,160 +88,84 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Toto tu nema byt ci? -->
|
||||
<!-- <div class="w-full">
|
||||
<A-task-timeblocks
|
||||
class="w-full"
|
||||
:timeblocks="subtask.timeblocks"
|
||||
:subtask="subtask"
|
||||
@update-subtask="$emit('update-subtask')"
|
||||
@update-unfinished-subtask="subtask = $event"
|
||||
@delete-timeblock="deleteTimeblock($event)"
|
||||
/>
|
||||
</div> -->
|
||||
<!-- Employee Assignment -->
|
||||
<A-task-employees
|
||||
v-model="subtask"
|
||||
:is-edit-mode="!!subtask.id"
|
||||
:validation-errors="employeeValidations"
|
||||
:custom-filter="customFilter"
|
||||
@remove-employee="handleRemoveEmployee"
|
||||
@clear-validation="clearEmployeeValidation"
|
||||
/>
|
||||
|
||||
<template v-if="subtask.id">
|
||||
<div
|
||||
class="employee-wrapper"
|
||||
:class="{ 'hide-arrow': subtask.employee_id }"
|
||||
>
|
||||
<svg
|
||||
<!-- Timeblocks per Employee -->
|
||||
<div class="employees-timeblocks w-100">
|
||||
<template v-if="subtask.id">
|
||||
<!-- Edit Mode: Single Employee -->
|
||||
<div
|
||||
v-if="subtask.employee_id"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
class="dp__icon dp__clear_icon dp__input_icons"
|
||||
data-test="clear-icon"
|
||||
@click.prevent="
|
||||
handleOnRemoveEmployee();
|
||||
subtask.employee_id = null;
|
||||
subtask.employee = null;
|
||||
"
|
||||
class="employee-timeblocks-section"
|
||||
>
|
||||
<path
|
||||
d="M23.057 7.057l-16 16c-0.52 0.52-0.52 1.365 0 1.885s1.365 0.52 1.885 0l16-16c0.52-0.52 0.52-1.365 0-1.885s-1.365-0.52-1.885 0z"
|
||||
></path>
|
||||
<path
|
||||
d="M7.057 8.943l16 16c0.52 0.52 1.365 0.52 1.885 0s0.52-1.365 0-1.885l-16-16c-0.52-0.52-1.365-0.52-1.885 0s-0.52 1.365 0 1.885z"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
<v-autocomplete
|
||||
v-model="subtask.employee_id"
|
||||
:custom-filter="customFilter"
|
||||
:match-any="true"
|
||||
label="Priradený"
|
||||
:items="enrichedEmployees"
|
||||
item-title="_fullName"
|
||||
item-value="id"
|
||||
variant="solo"
|
||||
class="modal-input"
|
||||
:class="{ error: employeeValidations[0]?.employee }"
|
||||
@update:model-value="
|
||||
employeeValidations[0]
|
||||
? (employeeValidations[0].employee = false)
|
||||
: null
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<v-text-field
|
||||
v-model="subtask.duration"
|
||||
class="modal-input"
|
||||
variant="solo"
|
||||
type="number"
|
||||
label="Časový odhad"
|
||||
:class="{ error: employeeValidations[0]?.duration }"
|
||||
append-inner-icon="mdi-clock-outline"
|
||||
@update:model-value="
|
||||
employeeValidations[0]
|
||||
? (employeeValidations[0].duration = false)
|
||||
: null
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="(employee, index) in subtask.employees"
|
||||
:key="`employee-${index}`"
|
||||
class="splitted-inputs remove-employee"
|
||||
:class="{ 'hide-arrow': employee?.id }"
|
||||
>
|
||||
<div class="relative left w-full">
|
||||
<v-autocomplete
|
||||
v-model="employee.id"
|
||||
:custom-filter="customFilter"
|
||||
:match-any="true"
|
||||
label="Priradený"
|
||||
:items="enrichedEmployees"
|
||||
item-title="_fullName"
|
||||
item-value="id"
|
||||
variant="solo"
|
||||
class="modal-input"
|
||||
:class="{ error: employeeValidations[index]?.employee }"
|
||||
@update:model-value="
|
||||
employeeValidations[index]
|
||||
? (employeeValidations[index].employee = false)
|
||||
: null
|
||||
<div class="employee-header">
|
||||
<h3>{{ getEmployeeFullName(subtask.employee_id) }}</h3>
|
||||
</div>
|
||||
<A-task-employee-timeline
|
||||
v-for="(timeblock, index) in getEmployeeTimeblocks(
|
||||
subtask.employee_id,
|
||||
)"
|
||||
:key="`timeblock-${index}`"
|
||||
:timeblock="timeblock"
|
||||
:employee="getEmployeeById(subtask.employee_id)"
|
||||
:deletable="
|
||||
getEmployeeTimeblocks(subtask.employee_id).length > 1
|
||||
"
|
||||
:index="index"
|
||||
@update-timeblock="
|
||||
(val) => updateTimeblock(val, index, subtask.employee_id)
|
||||
"
|
||||
@remove-timeblock="
|
||||
removeTimeblock(index, subtask.employee_id)
|
||||
"
|
||||
/>
|
||||
<svg
|
||||
v-if="employee.id"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
class="dp__icon dp__clear_icon dp__input_icons"
|
||||
data-test="clear-icon"
|
||||
@click.prevent="
|
||||
handleOnRemoveEmployee();
|
||||
employee.id = null;
|
||||
"
|
||||
>
|
||||
<path
|
||||
d="M23.057 7.057l-16 16c-0.52 0.52-0.52 1.365 0 1.885s1.365 0.52 1.885 0l16-16c0.52-0.52 0.52-1.365 0-1.885s-1.365-0.52-1.885 0z"
|
||||
></path>
|
||||
<path
|
||||
d="M7.057 8.943l16 16c0.52 0.52 1.365 0.52 1.885 0s0.52-1.365 0-1.885l-16-16c-0.52-0.52-1.365-0.52-1.885 0s-0.52 1.365 0 1.885z"
|
||||
></path>
|
||||
</svg>
|
||||
<div class="end-button w-100">
|
||||
<button @click="addTimeblock(subtask.employee_id)">
|
||||
Pridať časový blok
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<v-text-field
|
||||
v-model="employee.duration"
|
||||
class="modal-input right"
|
||||
variant="solo"
|
||||
type="number"
|
||||
label="Časový odhad"
|
||||
append-inner-icon="mdi-clock-outline"
|
||||
:class="{ error: employeeValidations[index]?.duration }"
|
||||
@update:model-value="
|
||||
employeeValidations[index]
|
||||
? (employeeValidations[index].duration = false)
|
||||
: null
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<div class="timeblocks w-100">
|
||||
<A-timeblock
|
||||
v-for="(timeblock, index) in editableTimeblocks"
|
||||
:key="index"
|
||||
:ref="`timetable-${index}`"
|
||||
:timeblock="timeblock"
|
||||
:deletable="index > 0"
|
||||
:editable="true"
|
||||
:index="index"
|
||||
@update-timeblock="(val) => updateTimeblock(val, index)"
|
||||
@remove-timeblock="removeTimeblock(index)"
|
||||
/>
|
||||
<div v-if="!subtask.id" class="end-button w-100">
|
||||
<button @click="addTimeblock">Pridať časový blok</button>
|
||||
</div>
|
||||
<template v-else>
|
||||
<!-- Create Mode: Multiple Employees -->
|
||||
<div
|
||||
v-for="employee in getValidEmployees()"
|
||||
:key="`employee-${employee.id}`"
|
||||
class="employee-timeblocks-section"
|
||||
>
|
||||
<div class="employee-header">
|
||||
<h3>{{ getEmployeeFullName(employee.id) }}</h3>
|
||||
</div>
|
||||
<A-task-employee-timeline
|
||||
v-for="(timeblock, index) in getEmployeeTimeblocks(
|
||||
employee.id,
|
||||
)"
|
||||
:key="`timeblock-${employee.id}-${index}`"
|
||||
:timeblock="timeblock"
|
||||
:employee="getEmployeeById(employee.id)"
|
||||
:deletable="getEmployeeTimeblocks(employee.id).length > 1"
|
||||
:index="index"
|
||||
@update-timeblock="
|
||||
(val) => updateTimeblock(val, index, employee.id)
|
||||
"
|
||||
@remove-timeblock="removeTimeblock(index, employee.id)"
|
||||
/>
|
||||
<div class="end-button w-100">
|
||||
<button @click="addTimeblock(employee.id)">
|
||||
Pridať časový blok
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="w-100 modal-input quill-editor-wrapper">
|
||||
@@ -278,10 +202,7 @@
|
||||
|
||||
<template v-else>
|
||||
<template v-if="subtask.is_draft">
|
||||
<v-btn
|
||||
class="button primary"
|
||||
:disabled="isSaving"
|
||||
@click="save(true)"
|
||||
<v-btn class="button primary" :disabled="isSaving" @click="save()"
|
||||
>ULOŽIŤ A PUBLIKOVAŤ</v-btn
|
||||
>
|
||||
|
||||
@@ -396,7 +317,8 @@ import '@vueup/vue-quill/dist/vue-quill.snow.css';
|
||||
import AutocompleteMixin from '@/plugins/tm@_mixins/autocomplete.mixin';
|
||||
|
||||
import ZTaskReadonlyInfo from './_components/z-task-readonly-info.vue';
|
||||
import ATimeblock from './_components/a-timeblock.vue';
|
||||
import ATaskEmployees from './_components/a-task-employees.vue';
|
||||
import ATaskEmployeeTimeline from './_components/a-task-employee-timeline.vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import modalMixin from '@/plugins/tm@modal/_mixins/modal.mixin';
|
||||
import AResizeHandle from '@/plugins/tm@modal/_components/a-resize-handle.vue';
|
||||
@@ -406,7 +328,8 @@ import ACloseButton from '@/plugins/tm@modal/_components/a-close-button.vue';
|
||||
export default {
|
||||
components: {
|
||||
ZTaskReadonlyInfo,
|
||||
ATimeblock,
|
||||
ATaskEmployees,
|
||||
ATaskEmployeeTimeline,
|
||||
ZFilesUpload,
|
||||
ZDatepicker,
|
||||
// ATaskTimeblocks,
|
||||
@@ -430,13 +353,7 @@ export default {
|
||||
employeeValidations: [],
|
||||
showDatePicker: false,
|
||||
selectedNewDate: [],
|
||||
timeblocks: [
|
||||
{
|
||||
date: null,
|
||||
starts_at_time: null,
|
||||
ends_at_time: null,
|
||||
},
|
||||
],
|
||||
timeblocks: [],
|
||||
wasFormValidationTriggered: false,
|
||||
modalName: 'z-task-modal',
|
||||
};
|
||||
@@ -450,27 +367,6 @@ 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() {
|
||||
@@ -494,13 +390,34 @@ export default {
|
||||
},
|
||||
|
||||
'subtask.employees': {
|
||||
handler() {
|
||||
handler(newEmployees, oldEmployees) {
|
||||
if (!this.subtask?.employees) return;
|
||||
|
||||
const lastEmployee =
|
||||
this.subtask.employees[this.subtask.employees.length - 1];
|
||||
if (lastEmployee?.id || lastEmployee?.duration)
|
||||
this.subtask.employees.push({});
|
||||
|
||||
// Auto-create first timeblock when employee gets both id and duration
|
||||
if (newEmployees && oldEmployees) {
|
||||
newEmployees.forEach((newEmp, index) => {
|
||||
const oldEmp = oldEmployees[index] || {};
|
||||
// Check if employee just became "complete" (has both id and duration)
|
||||
const wasComplete = oldEmp.id && oldEmp.duration;
|
||||
const isComplete = newEmp.id && newEmp.duration;
|
||||
|
||||
if (!wasComplete && isComplete) {
|
||||
// Check if this employee already has timeblocks
|
||||
const hasTimeblocks = this.timeblocks.some(
|
||||
(tb) => tb.employee_id === newEmp.id,
|
||||
);
|
||||
if (!hasTimeblocks) {
|
||||
// Add first timeblock for this employee
|
||||
this.addTimeblock(newEmp.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
@@ -567,7 +484,10 @@ export default {
|
||||
data.timeblocks.forEach((timeblock) => {
|
||||
// If already in internal format, keep as is
|
||||
if (timeblock.date) {
|
||||
convertedTimeblocks.push(timeblock);
|
||||
convertedTimeblocks.push({
|
||||
...timeblock,
|
||||
employee_id: data.employee_id, // Add employee_id
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -581,11 +501,13 @@ export default {
|
||||
starts_at_time: startMoment.format('HH:mm'),
|
||||
ends_at_time: endMoment.format('HH:mm'),
|
||||
is_finished: timeblock.is_finished || 0,
|
||||
employee_id: data.employee_id, // Add employee_id
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
data.timeblocks = convertedTimeblocks;
|
||||
// Store in component's timeblocks array
|
||||
this.timeblocks = convertedTimeblocks;
|
||||
|
||||
this.subtask = this._enrichTask(data);
|
||||
|
||||
@@ -612,42 +534,92 @@ export default {
|
||||
this.$refs[ref].openMenu();
|
||||
},
|
||||
|
||||
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;
|
||||
// Helper: Get employee by ID
|
||||
getEmployeeById(employeeId) {
|
||||
return this.enrichedEmployees.find((emp) => emp.id === employeeId);
|
||||
},
|
||||
|
||||
// Helper: Get employee full name
|
||||
getEmployeeFullName(employeeId) {
|
||||
const employee = this.getEmployeeById(employeeId);
|
||||
return employee ? `${employee.name} ${employee.surname}` : '';
|
||||
},
|
||||
|
||||
// Helper: Get valid employees (with id and duration) in create mode
|
||||
getValidEmployees() {
|
||||
if (!this.subtask?.employees) return [];
|
||||
return this.subtask.employees.filter((emp) => emp.id && emp.duration);
|
||||
},
|
||||
|
||||
// Helper: Get timeblocks for specific employee
|
||||
getEmployeeTimeblocks(employeeId) {
|
||||
if (!this.timeblocks) return [];
|
||||
return this.timeblocks.filter((tb) => tb.employee_id === employeeId);
|
||||
},
|
||||
|
||||
// Clear employee validation
|
||||
clearEmployeeValidation(index) {
|
||||
if (index !== undefined && this.employeeValidations[index]) {
|
||||
this.employeeValidations[index].employee = false;
|
||||
this.employeeValidations[index].duration = false;
|
||||
} else if (index === undefined && this.employeeValidations[0]) {
|
||||
this.employeeValidations[0].employee = false;
|
||||
this.employeeValidations[0].duration = false;
|
||||
}
|
||||
},
|
||||
|
||||
addTimeblock() {
|
||||
updateTimeblock(val, index, employeeId) {
|
||||
const employeeTimeblocks = this.getEmployeeTimeblocks(employeeId);
|
||||
const timeblock = employeeTimeblocks[index];
|
||||
|
||||
if (!timeblock) return;
|
||||
|
||||
// Find the actual index in the full timeblocks array
|
||||
const actualIndex = this.timeblocks.indexOf(timeblock);
|
||||
|
||||
if (actualIndex !== -1) {
|
||||
this.timeblocks[actualIndex] = { ...val, employee_id: employeeId };
|
||||
}
|
||||
},
|
||||
|
||||
addTimeblock(employeeId) {
|
||||
const newTimeblock = {
|
||||
date: null,
|
||||
date: new Date(), // Preset to current day
|
||||
starts_at_time: null,
|
||||
ends_at_time: null,
|
||||
employee_id: employeeId,
|
||||
};
|
||||
|
||||
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);
|
||||
this.timeblocks.push(newTimeblock);
|
||||
},
|
||||
|
||||
removeTimeblock(index, employeeId) {
|
||||
const employeeTimeblocks = this.getEmployeeTimeblocks(employeeId);
|
||||
const timeblock = employeeTimeblocks[index];
|
||||
|
||||
if (!timeblock) return;
|
||||
|
||||
// Find the actual index in the full timeblocks array
|
||||
const actualIndex = this.timeblocks.indexOf(timeblock);
|
||||
|
||||
if (actualIndex !== -1) {
|
||||
this.timeblocks.splice(actualIndex, 1);
|
||||
}
|
||||
},
|
||||
|
||||
removeTimeblock(index) {
|
||||
if (this.subtask?.id) {
|
||||
// Update mode
|
||||
this.subtask.timeblocks.splice(index, 1);
|
||||
} else {
|
||||
// Create mode
|
||||
this.timeblocks.splice(index, 1);
|
||||
// Handle remove employee - also removes their timeblocks
|
||||
handleRemoveEmployee(employeeId) {
|
||||
if (employeeId) {
|
||||
// Remove all timeblocks for this employee
|
||||
this.timeblocks = this.timeblocks.filter(
|
||||
(tb) => tb.employee_id !== employeeId,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.wasFormValidationTriggered) {
|
||||
setTimeout(async () => {
|
||||
this.validateAllFields();
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -659,13 +631,7 @@ export default {
|
||||
this.preventInputFocus = true;
|
||||
|
||||
this.visible = true;
|
||||
this.timeblocks = [
|
||||
{
|
||||
date: null,
|
||||
starts_at_time: null,
|
||||
ends_at_time: null,
|
||||
},
|
||||
];
|
||||
this.timeblocks = [];
|
||||
this.$nextTick(() => {
|
||||
this.v$.$reset();
|
||||
|
||||
@@ -687,7 +653,10 @@ export default {
|
||||
data.timeblocks.forEach((timeblock) => {
|
||||
// If already in internal format, keep as is
|
||||
if (timeblock.date) {
|
||||
convertedTimeblocks.push(timeblock);
|
||||
convertedTimeblocks.push({
|
||||
...timeblock,
|
||||
employee_id: data.employee_id, // Add employee_id for edit mode
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -704,11 +673,18 @@ export default {
|
||||
starts_at_time: startMoment.format('HH:mm'),
|
||||
ends_at_time: endMoment.format('HH:mm'),
|
||||
is_finished: timeblock.is_finished || 0,
|
||||
employee_id: data.employee_id, // Add employee_id for edit mode
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
data.timeblocks = convertedTimeblocks;
|
||||
// Store timeblocks in the component's timeblocks array
|
||||
this.timeblocks = convertedTimeblocks;
|
||||
|
||||
// Ensure at least one timeblock exists for edit mode
|
||||
if (data.employee_id && this.timeblocks.length === 0) {
|
||||
this.addTimeblock(data.employee_id);
|
||||
}
|
||||
|
||||
if (!data.deadline) {
|
||||
this.editMode = editMode || !data.id;
|
||||
@@ -796,14 +772,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async handleOnRemoveEmployee() {
|
||||
if (this.wasFormValidationTriggered) {
|
||||
setTimeout(async () => {
|
||||
this.validateAllFields();
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
|
||||
async save(concept = false) {
|
||||
this.wasFormValidationTriggered = true;
|
||||
if (!(await this.validateAllFields(concept))) return;
|
||||
@@ -826,8 +794,12 @@ export default {
|
||||
});
|
||||
|
||||
// Convert timeblocks from internal format to BE format for update
|
||||
const timeblocks = (this.subtask.timeblocks || [])
|
||||
.filter((timeblock) => timeblock.date) // Only send timeblocks with dates
|
||||
const timeblocks = this.timeblocks
|
||||
.filter(
|
||||
(timeblock) =>
|
||||
timeblock.date &&
|
||||
timeblock.employee_id === this.subtask.employee_id,
|
||||
) // Only send timeblocks for this employee
|
||||
.map((timeblock) => {
|
||||
const dateStr = moment(timeblock.date).format('YYYY-MM-DD');
|
||||
return {
|
||||
@@ -839,10 +811,17 @@ export default {
|
||||
});
|
||||
|
||||
const payload = { ...this.subtask, timesheets, timeblocks };
|
||||
|
||||
console.log('isConcept', concept);
|
||||
|
||||
if (concept) {
|
||||
payload.concept = true;
|
||||
payload.is_draft = 1;
|
||||
} else {
|
||||
payload.is_draft = 0;
|
||||
}
|
||||
|
||||
console.log('payload', payload);
|
||||
|
||||
const { subtask } = await this.$axios.patch_auth_form(
|
||||
`v1/subtasks/${this.subtask.id}`,
|
||||
payload,
|
||||
@@ -857,9 +836,13 @@ export default {
|
||||
(employee) => employee.id && employee.duration,
|
||||
);
|
||||
|
||||
// Assign timeblocks to each employee based on employee_id
|
||||
validEmployees.forEach((employee) => {
|
||||
employee.timeblocks = this.timeblocks
|
||||
.filter((timeblock) => timeblock.date)
|
||||
.filter(
|
||||
(timeblock) =>
|
||||
timeblock.date && timeblock.employee_id === employee.id,
|
||||
)
|
||||
.map((timeblock) => {
|
||||
return {
|
||||
start: `${moment(timeblock.date).format('YYYY-MM-DD')} ${
|
||||
@@ -995,18 +978,37 @@ export default {
|
||||
},
|
||||
|
||||
async _validateTimeblocks() {
|
||||
// Validate that all timeblocks have required fields and valid time ranges
|
||||
let valid = true;
|
||||
const timeblocks = this.editableTimeblocks;
|
||||
|
||||
for (let i = 0; i < timeblocks.length; i++) {
|
||||
const ref = this.$refs[`timetable-${i}`];
|
||||
if (ref && ref[0]) {
|
||||
const isValid = await ref[0].v$.$validate();
|
||||
if (!isValid) {
|
||||
valid = false;
|
||||
}
|
||||
for (const timeblock of this.timeblocks) {
|
||||
if (
|
||||
!timeblock.date ||
|
||||
!timeblock.starts_at_time ||
|
||||
!timeblock.ends_at_time
|
||||
) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Validate that end time is after start time
|
||||
const dateStr = moment(timeblock.date).format('YYYY-MM-DD');
|
||||
const start = moment(
|
||||
`${dateStr} ${timeblock.starts_at_time}`,
|
||||
'YYYY-MM-DD HH:mm',
|
||||
);
|
||||
const end = moment(
|
||||
`${dateStr} ${timeblock.ends_at_time}`,
|
||||
'YYYY-MM-DD HH:mm',
|
||||
);
|
||||
|
||||
if (end.isSameOrBefore(start)) {
|
||||
this.$toast.error('Koniec času musí byť po začiatku');
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
},
|
||||
|
||||
@@ -1091,32 +1093,57 @@ export default {
|
||||
.files
|
||||
margin-top: 104px !important
|
||||
|
||||
.timeblocks
|
||||
.employees-timeblocks
|
||||
display: flex
|
||||
flex-direction: column
|
||||
width: 100%
|
||||
margin-top: 20px
|
||||
|
||||
.employee-timeblocks-section
|
||||
width: 100%
|
||||
margin-bottom: 30px
|
||||
border: 1px solid #e0e0e0
|
||||
border-radius: 8px
|
||||
padding: 20px
|
||||
background: #fafafa
|
||||
|
||||
.employee-header
|
||||
margin-bottom: 15px
|
||||
padding-bottom: 10px
|
||||
border-bottom: 2px solid #7f39fb
|
||||
|
||||
h3
|
||||
margin: 0
|
||||
color: #7f39fb
|
||||
font-size: 18px
|
||||
font-weight: 600
|
||||
|
||||
.end-button
|
||||
width: 100%
|
||||
display: flex
|
||||
justify-content: flex-end
|
||||
align-items: center
|
||||
border-bottom: 1px solid #e0e0e0
|
||||
margin-top: 20px
|
||||
padding-top: 20px
|
||||
border-top: 1px solid #e0e0e0
|
||||
|
||||
.end-button
|
||||
width: 100%
|
||||
display: flex
|
||||
justify-content: flex-end
|
||||
align-items: center
|
||||
border-top: 1px solid #e0e0e0
|
||||
height: 70px
|
||||
button
|
||||
background: #7f39fb !important
|
||||
border-radius: 4px !important
|
||||
color: #fff !important
|
||||
height: 50px
|
||||
width: 203px
|
||||
border: none
|
||||
cursor: pointer
|
||||
transition: all 0.2s
|
||||
font-size: 14px
|
||||
font-weight: 500
|
||||
|
||||
button
|
||||
background: #7f39fb !important
|
||||
border-radius: 4px !important
|
||||
color: #fff !important
|
||||
height: 50px
|
||||
width: 203px
|
||||
&:hover
|
||||
background: #6b2dd9 !important
|
||||
|
||||
.splitted-time-inputs
|
||||
display: flex !important
|
||||
flex-direction: row !important
|
||||
gap: 15px
|
||||
margin-bottom: -11px
|
||||
&:active
|
||||
background: #5a24b8 !important
|
||||
|
||||
.timeblock
|
||||
position: relative
|
||||
|
||||
Reference in new issue
Block a user