Files
jandl-taskmanager-app/app/src/plugins/tm@modal/task/z-task-modal.vue
T

1006 lines
30 KiB
Vue

<template>
<v-dialog
v-model="visible"
max-width="750"
max-height="90%"
height="700px"
persistent
>
<W-loadable :content="subtask" class="h-100">
<v-card
v-if="editMode"
height="100%"
class="d-flex flex-column scrolling"
>
<div class="d-flex justify-space-between align-center modal-header">
<h1>{{ subtask?.id ? 'Upraviť sub task' : 'Vytvoriť sub task' }}</h1>
<v-icon
@click.stop="closeModal()"
icon="mdi-close"
size="large"
></v-icon>
</div>
<v-card-actions class="d-flex flex-column modal-content">
<v-text-field
v-model="subtask.project_code"
label="Kód projektu"
:value="subtask.project_code"
@focus="onInputFocus"
class="modal-input"
variant="solo"
disabled
></v-text-field>
<v-autocomplete
:custom-filter="customFilter"
:match-any="true"
v-model="subtask.master_task_id"
label="Master task"
:class="{ error: v$.subtask.master_task_id.$errors.length }"
:items="mastertasks"
item-title="name"
item-value="id"
variant="solo"
class="modal-input"
clearable
/>
<v-text-field
v-model="subtask.title"
@focus="onInputFocus"
class="modal-input"
label="Názov subtasku"
variant="solo"
></v-text-field>
<div class="splitted-inputs">
<!-- TODO: Bialko validacie -->
<div
@click="openDatepicker('datepicker-01')"
class="datepicker-wrapper"
>
<Datepicker
v-model="subtask.deadline"
@focus="onDateFocus"
ref="datepicker-01"
:auto-apply="true"
:min-date="new Date()"
class="modal-input left datepicker-input"
format="dd.MM.yyyy"
locale="sk"
select-text="Vybrať"
cancel-text="Zrušiť"
placeholder="Deadline"
:class="{ error: v$.subtask.deadline.$errors?.length }"
:validations="v$.subtask.deadline"
/>
</div>
<v-text-field
class="modal-input right"
variant="solo"
v-model="subtask._deadline_time"
label="Čas"
append-inner-icon="mdi-clock-outline"
type="time"
@focus="onInputFocus"
:class="{ error: v$.subtask._deadline_time.$errors.length }"
:hint="
v$.subtask._deadline_time.$errors
? errorUtils.parseErrors(v$.subtask._deadline_time.$errors)
: null
"
/>
</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> -->
<template v-if="subtask.id">
<div
class="employee-wrapper"
:class="{ 'hide-arrow': subtask.employee_id }"
>
<svg
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;
"
>
<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
:custom-filter="customFilter"
:match-any="true"
v-model="subtask.employee_id"
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"
@focus="onInputFocus"
@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 }"
>
<v-autocomplete
:custom-filter="customFilter"
:match-any="true"
v-model="employee.id"
label="Priradený"
:items="enrichedEmployees"
item-title="_fullName"
item-value="id"
variant="solo"
class="modal-input left"
:class="{ error: employeeValidations[index]?.employee }"
@update:model-value="
employeeValidations[index]
? (employeeValidations[index].employee = false)
: null
"
/>
<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>
<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 }"
@focus="onInputFocus"
@update:model-value="
employeeValidations[index]
? (employeeValidations[index].duration = false)
: null
"
/>
</div>
</template>
<!-- <div class="w-100">
<Datepicker v-model="subtask.date" :class="{ error: v$.subtask.date.$errors.length }" format="dd.MM.yyyy" locale="sk" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Dátum" :auto-apply="true" :enable-time-picker="false" @focus="onDateFocus" class="mb-5 modal-input" />
<div class="splitted-time-inputs">
<v-text-field :class="{ error: v$.subtask.starts_at_time.$errors.length }" type="time" variant="solo" bg-color="white" label="Začiatok" />
<v-text-field :class="{ error: v$.subtask.ends_at_time.$errors.length }" type="time" variant="solo" bg-color="white" label="Koniec" />
</div>
</div> -->
<template v-if="!subtask.id">
<div class="timeblocks w-100">
<A-timeblock
v-for="(timeblock, index) in timeblocks"
:key="index"
@updateTimeblock="(val) => updateTimeblock(val, index)"
@removeTimeblock="removeTimeblock(index)"
:timeblock="timeblock"
:deletable="index > 0"
editable
:ref="`timetable-${index}`"
/>
<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">
<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 class="w-100 modal-input">
<QuillEditor
ref="description"
v-model="subtask.description"
placeholder="Popis"
theme="snow"
class="rich-text-editor"
@input="subtask.description = $event.srcElement.innerHTML"
/>
</div>
<Z-files-upload
class="mt-15 files"
:files="subtask.files"
@files-uploaded="filesUploaded"
:is-saving="isSaving"
/>
</v-card-actions>
<v-btn class="button primary" v-if="!isSaving" @click="save()">{{
subtask.id ? 'UPRAVIŤ' : 'VYTVORIŤ'
}}</v-btn>
<v-btn class="button primary disabled" v-else disabled>{{
$t('Ukladám...')
}}</v-btn>
</v-card>
<v-card v-else height="100%" class="d-flex flex-column scrolling">
<div class="d-flex justify-space-between align-center">
<div>
<p>{{ subtask._master_task.project_code }}</p>
<h1>{{ subtask._master_task.name }}</h1>
</div>
<v-icon
@click.stop="closeModal()"
icon="mdi-close"
size="large"
class="pb-5"
></v-icon>
</div>
<div
class="d-flex justify-space-between align-center modal-header border-bottom pb-4"
>
<h1>{{ subtask.title }}</h1>
<div class="d-flex justify-start">
<img
class="icon"
:src="require('@/plugins/app/_img/icons/pencil.svg')"
alt="pencil-icon"
@click.stop="toggleEditMode"
/>
<img
class="icon trash"
:src="require('@/plugins/app/_img/icons/bin.svg')"
alt="trash-icon"
@click.stop="openDeleteTaskModal(subtask)"
/>
</div>
</div>
<v-card-actions class="d-flex flex-column modal-content">
<div class="w-100">
<Z-task-readonly-info
:subtask="subtask"
@close-modal="closeModal"
@update-subtask="updateSubtask"
/>
<!-- <div class="mt-10">
<p>Termín</p>
<div class="date-wrap">
<Datepicker @open="editDatepicker('date')" v-model="subtask.deadline" @focus="onDateFocus" :min-date="new Date()" class="modal-input" format="dd.MM.yyyy" locale="sk" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Dátum" />
<Datepicker @open="editDatepicker('time')" v-model="subtask.deadline" @focus="onDateFocus" :min-date="new Date()" class="modal-input" format="HH:mm" locale="sk" select-text="Vybrať" cancel-text="Zrušiť" placeholder="Čas" />
</div>
</div> -->
<div class="d-flex justify-start align-center mt-8">
<v-btn
class="done-btn"
v-if="!subtask.is_finished"
@click.stop="showCompleteTaskModal()"
>Dokončiť</v-btn
>
<v-btn
class="button secondary"
v-else
@click.stop="showCompleteTaskModal()"
>Znovu otvoriť</v-btn
>
<v-btn class="button secondary" @click="closeModal()"
>Uložiť zmeny</v-btn
>
</div>
</div>
</v-card-actions>
</v-card>
</W-loadable>
</v-dialog>
</template>
<script>
import Datepicker from '@vuepic/vue-datepicker';
import ZFilesUpload from '@/plugins/tm@_components/z-files-upload.vue';
import ATaskTimeblocks from '@/plugins/tm@modal/task/_components/_components/a-task-timeblocks.vue';
import useVuelidate from '@vuelidate/core';
import { required, requiredIf, optional } from '@vuelidate/validators';
import errorUtils from '@/plugins/lib@auth/_utils/error.utils';
import { QuillEditor } from '@vueup/vue-quill';
import VuetifyFixFocusMixin from '@/plugins/tm@modal/_mixins/vuetify-fix-focus';
import SelectsMixin from '../master-task/_mixins/selects.mixin';
import * as FileSaver from 'file-saver';
import tmUtils from '@/plugins/tm@_utils/tm.utils';
import moment from 'moment';
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 { mapGetters } from 'vuex';
import modalMixin from '@/plugins/tm@modal/_mixins/modal.mixin';
export default {
emits: ['close', 'refresh'],
mixins: [VuetifyFixFocusMixin, SelectsMixin, AutocompleteMixin, modalMixin],
components: {
ZTaskReadonlyInfo,
ATimeblock,
ZFilesUpload,
Datepicker,
// ATaskTimeblocks,
QuillEditor,
},
data() {
return {
tmUtils,
editMode: false,
visible: false,
subtask: null,
isSaving: false,
errorUtils,
v$: useVuelidate(),
employeeValidations: [],
showDatePicker: false,
selectedNewDate: [],
timeblocks: [
{
date: null,
starts_at_time: null,
ends_at_time: null,
},
],
wasFormValidationTriggered: false,
};
},
computed: {
...mapGetters('tm', ['mastertasks', 'enrichedEmployees']),
},
validations() {
return {
subtask: {
master_task_id: { required },
deadline: { required },
_deadline_time: { required },
},
};
},
mounted() {
this.$events.on('add-timeblock', (timeblock) => {
if (!this.subtask?.timeblocks) {
this.subtask.timeblocks = [];
}
if (!this.subtask?.timesheets) {
this.subtask.timesheets = [];
}
if (!this.subtask?.is_finished) {
this.subtask.is_finished = 0;
}
this.subtask.timeblocks.push(timeblock);
});
this.$events.on('on-subtask-edited', (subtask) => {
if (subtask?.id !== this.subtask?.id) return;
const data = JSON.parse(JSON.stringify(subtask));
const splittedDeadline = data.deadline?.split(' ') || [];
data.deadline = splittedDeadline[0];
data._deadline_time = splittedDeadline[1];
this.subtask = this._enrichTask(data);
// TODO: Remove? riesil som neodstranovanie jedineho zanamu v trvani a odpracovanom case
console.log('2', this.subtask);
this.$forceUpdate();
});
this.$events.on('on-subtask-deleted', (subtask) => {
if (subtask?.id == this.subtask?.id) this.closeModal();
});
},
methods: {
toggleEditMode() {
if (this.subtask.type == 'office365')
return this.$events.emit('show-unable-to-edit-office-task-modal', {
mode: 'edit',
});
this.editMode = !this.editMode;
},
openDatepicker(ref) {
this.$refs[ref].openMenu();
},
updateTimeblock(val, index) {
this.timeblocks[index] = val;
},
removeTimeblock(index) {
this.timeblocks.splice(index, 1);
},
async showModal({ subtask, editMode = false, mastertask = {} }) {
this.isSaving = false;
this.subtask = null;
this.visible = true;
this.timeblocks = [
{
date: null,
starts_at_time: null,
ends_at_time: null,
},
];
this.$nextTick(() => {
this.v$.$reset();
this.setFocusEvent();
});
try {
if (subtask?.id) {
const data = await this.$axios.get_auth_data(
`v1/subtasks/${subtask.id}`,
);
if (!data.deadline) {
this.editMode = editMode || !data.id;
return (this.subtask = this._enrichTask(data));
}
const splittedDeadline = data.deadline.split(' ');
data.deadline = splittedDeadline[0];
data._deadline_time = splittedDeadline[1];
this.subtask = this._enrichTask(data);
} else {
const savedSubtask = localStorage.getItem('subtaskData');
try {
if (savedSubtask) {
const parsedSubtask = JSON.parse(savedSubtask);
this.subtask = parsedSubtask;
// If the parsed result is not an object, reset to default
if (typeof this.subtask !== 'object' || this.subtask === null) {
throw new Error('Parsed subtask is not an object');
}
this.$nextTick(() => {
this.$refs['description']?.setHTML(
this.subtask.description || '',
);
});
} else {
throw new Error('subtaskData is null or undefined');
}
} catch (error) {
console.error('Error parsing subtaskData:', error);
this.subtask = {
master_task: mastertask,
employees: [{}],
_deadline_time: '17:00',
};
}
if (
subtask?.deadline &&
subtask._deadline_time &&
subtask.employee_id
) {
const employee = this.enrichedEmployees.find(
(employee) => employee.id == subtask.employee_id,
);
this.subtask.employees = [employee];
this.subtask.deadline = subtask.deadline;
this.subtask._deadline_time = subtask._deadline_time;
}
if (subtask?.master_task_id && subtask?.project_code) {
this.subtask.master_task_id = subtask.master_task_id;
this.subtask.project_code = subtask.project_code;
}
}
this.editMode = editMode || !this.subtask.id;
} catch (error) {
this.$toast.error(this.$parseError(error));
if (process.env.NODE_ENV === 'development')
console.log('[z-task-modal.vue] Error: ', error);
}
},
deleteTimeblock(i) {
this.subtask.timeblocks.splice(i, 1);
},
closeModal() {
this.visible = false;
this.$router.replace({ name: this.$route.name });
},
async validateAllFields() {
const employeesValid = await this._validateEmployees();
const timeblocksValid = this.subtask?.id
? true
: await this._validateTimeblocks();
if (!(await this.v$.$validate())) return;
if (!employeesValid || !timeblocksValid) return;
return true;
},
async handleOnRemoveEmployee() {
if (this.wasFormValidationTriggered) {
setTimeout(async () => {
this.validateAllFields();
}, 100);
}
},
async save() {
this.wasFormValidationTriggered = true;
if (!(await this.validateAllFields())) return;
this.subtask.description = this.$refs['description'].getHTML();
this._mergeDeadlineTimeDate();
try {
this.isSaving = true;
this.subtask.deadline = moment(this.subtask.deadline).format(
'YYYY-MM-DD HH:mm:SS',
);
if (this.subtask?.id) {
const timesheets = this.subtask.timesheets.map((timesheet) => {
delete timesheet.employee_name;
return timesheet;
});
const { subtask } = await this.$axios.patch_auth_form(
`v1/subtasks/${this.subtask.id}`,
{ ...this.subtask, timesheets },
);
localStorage.removeItem('subtaskData');
this.$store.commit('tm/update_subtask', subtask);
this.$events.emit('on-subtask-edited', this.subtask);
} else {
// Filter out employees that don't have both id and duration
const validEmployees = this.subtask.employees.filter(
(employee) => employee.id && employee.duration,
);
validEmployees.forEach((employee) => {
employee.timeblocks = this.timeblocks
.filter((timeblock) => timeblock.date)
.map((timeblock) => {
return {
start: `${moment(timeblock.date).format('YYYY-MM-DD')} ${
timeblock.starts_at_time
}:00`,
end: `${moment(timeblock.date).format('YYYY-MM-DD')} ${
timeblock.ends_at_time
}:00`,
id: null,
};
});
});
const subtaskCopy = {
...this.subtask,
employees: validEmployees,
};
delete subtaskCopy._master_task;
const { subtasks } = await this.$axios.post_auth(
`v1/mastertasks/${this.subtask.master_task_id}/subtasks`,
subtaskCopy,
);
localStorage.removeItem('subtaskData');
this.subtask.id = subtasks[0].id;
subtasks.forEach((subtask) => {
this.$store.commit('tm/update_subtask', subtask);
this.$events.emit('on-subtask-edited', this.subtask);
});
this.$events.emit('task-created');
this.$events.emit('close-both-modals');
}
this.closeModal();
} catch (error) {
this.$toast.error(this.$parseError(error));
if (process.env.NODE_ENV === 'development')
console.log('[z-task-modal.vue] Error: ', error);
} finally {
this.isSaving = false;
}
},
openDeleteTaskModal(subtask) {
if (subtask.type == 'office365')
return this.$events.emit('show-unable-to-edit-office-task-modal', {
mode: 'delete',
});
this.$events.emit('show-delete-task-modal', { subtask });
},
showCompleteTaskModal() {
this.$events.emit('show-complete-task-modal', { subtask: this.subtask });
if (this.subtask.deadline)
this.subtask.deadline = tmUtils.formatDateTimeForBackend(
this.subtask.deadline,
);
},
filesUploaded(files) {
this.subtask.files = files;
},
updateSubtask(subtask) {
this.subtask = subtask;
},
_mergeDeadlineTimeDate() {
const date = new Date(this.subtask.deadline);
console.log('z-task-modal', this.subtask);
const timeParts = this.subtask._deadline_time.split(':');
date.setHours(parseInt(timeParts[0]));
date.setMinutes(parseInt(timeParts[1]));
this.subtask.deadline = date;
},
_validateEmployees() {
return new Promise((resolve) => {
this.employeeValidations = [];
if (this.subtask.id) {
// For existing subtasks, require both employee and duration
if (!this.subtask.employee_id || !this.subtask.duration) {
this.employeeValidations[0] = {
employee: !this.subtask.employee_id,
duration: !this.subtask.duration,
};
}
} else {
// For new subtasks, require at least one employee with duration
let hasValidEmployee = false;
this.subtask.employees.forEach((employee, index) => {
// Skip validation if both id and duration are empty
if (!employee.id && !employee.duration) {
// If this is the first employee and no valid employee exists yet, show validation
if (index === 0 && !hasValidEmployee) {
this.employeeValidations[index] = {
employee: true,
duration: true,
};
}
return;
}
// Validate if either id or duration is filled
if (employee.id || employee.duration) {
this.employeeValidations[index] = {
employee: !employee.id,
duration: !employee.duration,
};
// If both id and duration are present, mark as valid
if (employee.id && employee.duration) {
hasValidEmployee = true;
}
}
});
}
const isError = this.employeeValidations.some(
(employee) => employee.duration || employee.employee,
);
resolve(!isError);
});
},
async _validateTimeblocks() {
let invalid = false;
for (let i = 0; i < this.timeblocks.length; i++) {
const ref = this.$refs[`timetable-${i}`];
invalid = (await ref[0].v$.$validate()) || invalid;
}
return invalid;
},
_enrichTask(subtask) {
return {
...subtask,
_created_by: this.enrichedEmployees.find(
(employee) => employee.id == subtask.created_by,
),
_employee: this.enrichedEmployees.find(
(employee) => employee.id == subtask.employee_id,
),
_master_task: this.mastertasks.find(
(mastertask) => mastertask.id == subtask.master_task_id,
),
};
},
},
watch: {
'subtask.employee_id'(value, oldValue) {
if (!value || !oldValue) return;
this.subtask.timeblocks = [];
this.subtask._employee = this.enrichedEmployees.find(
(employee) => employee.id == this.subtask.employee_id,
);
},
'subtask.employees': {
handler() {
if (!this.subtask?.employees) return;
const lastEmployee =
this.subtask.employees[this.subtask.employees.length - 1];
if (lastEmployee.id || lastEmployee.duration)
this.subtask.employees.push({});
},
deep: true,
},
'subtask.master_task_id'(value) {
if (!value) return;
this.subtask._master_task = this.mastertasks.find(
(mastertask) => mastertask.id == this.subtask.master_task_id,
);
this.subtask.project_code =
this.subtask._master_task?.project_code || null;
},
editMode() {
this.$nextTick(() => {
this.$refs['description']?.setHTML(this.subtask.description || '');
});
},
subtask: {
handler(val) {
if (this.editMode && !this.subtask?.id) {
const employees = val?.employees?.filter(
(employee) => employee?.id || employee?.duration,
) || [{}];
const subtaskData = {
...val,
employees: employees && employees.length > 0 ? employees : [{}],
};
console.log('subtaskData', subtaskData);
localStorage.setItem('subtaskData', JSON.stringify(subtaskData));
}
},
deep: true,
},
},
};
</script>
<style lang="sass" scoped>
.w-full
width: 100%
.date-wrap
display: flex
flex-flow: row
width: 100%
gap: 20px
margin-bottom: 20px
.dp__main
width: calc(50% - 10px) !important
.scrolling
overflow: hidden !important
.border-bottom
border-bottom: 1px solid rgba(0,0,0,.1)
.done-btn
background: #7f39fb !important
border-radius: 4px !important
color: #fff !important
height: 50px
width: 203px
.remove-employee
position: relative
width: 100%
svg
position: absolute
right: 205px
top: 28px
z-index: 5
opacity: .5
&.hide-arrow ::v-deep
.mdi-menu-down,
.mdi-menu-up
display: none
.employee-wrapper
position: relative
width: 100%
svg
position: absolute
right: 4px
top: 28px
z-index: 5
opacity: .5
&.hide-arrow ::v-deep
.mdi-menu-down,
.mdi-menu-up
display: none
@media screen and (max-width: 522px)
.files
margin-top: 104px !important
.timeblocks
display: flex
flex-direction: column
align-items: center
border-bottom: 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
.splitted-time-inputs
display: flex !important
flex-direction: row !important
gap: 15px
margin-bottom: -11px
.timeblock
position: relative
border-top: 1px solid #e0e0e0
padding: 20px 0 10px 0
.splitted-time-inputs
display: flex !important
flex-direction: row !important
gap: 15px
margin-bottom: -11px
.datepicker-wrapper
width: 70%
.datepicker-input
width: 100% !important
pointer-events: none
</style>