mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 14:29:34 +00:00
build(dev): finished timeblocks rework
This commit is contained in:
1 parent
1b0a9d5fff
commit
90dd4ee4b6
15 files changed
+194
-166
No files matched your search
@@ -68,6 +68,9 @@ th
|
||||
.v-card
|
||||
padding: 35px !important
|
||||
|
||||
@media (max-width: 768px)
|
||||
padding: 16px !important
|
||||
|
||||
.cursor-pointer:hover
|
||||
cursor: pointer
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from '@tanstack/vue-query';
|
||||
import { computed, Ref, unref } from 'vue';
|
||||
import { computed, Ref, unref, watch } from 'vue';
|
||||
import axios from '@/plugins/w@custom/axios/models/axios';
|
||||
import moment from 'moment';
|
||||
import timelineUtils from '@/plugins/tm@dashboard/tasks/_utils/timeline.utils';
|
||||
@@ -130,7 +130,6 @@ export function useEmployeeTimeline(
|
||||
const scheduledTime = computed(() => {
|
||||
const tasks = unref(timelineTasks);
|
||||
const d = unref(date);
|
||||
const now = moment();
|
||||
|
||||
if (!tasks || !d) return 0;
|
||||
|
||||
@@ -145,25 +144,10 @@ export function useEmployeeTimeline(
|
||||
return;
|
||||
}
|
||||
|
||||
if (now.isAfter(moment(task._real_end))) {
|
||||
// Ignore elapsed tasks
|
||||
return;
|
||||
} else if (
|
||||
now.isAfter(moment(task._real_start)) &&
|
||||
now.isBefore(moment(task._real_end))
|
||||
) {
|
||||
// Task in progress
|
||||
totalMinutes += timelineUtils._getDifferenceInMinutes(
|
||||
task._real_end,
|
||||
new Date(),
|
||||
);
|
||||
} else {
|
||||
// Future task
|
||||
totalMinutes += timelineUtils._getDifferenceInMinutes(
|
||||
task._real_end,
|
||||
task._real_start,
|
||||
);
|
||||
}
|
||||
totalMinutes += timelineUtils._getDifferenceInMinutes(
|
||||
task._real_end,
|
||||
task._real_start,
|
||||
);
|
||||
});
|
||||
|
||||
return (totalMinutes / 60).toFixed(1);
|
||||
@@ -177,28 +161,9 @@ export function useEmployeeTimeline(
|
||||
|
||||
if (!emp || !d) return '0';
|
||||
|
||||
const now = moment();
|
||||
const dateObj = moment(d);
|
||||
const dailyCapacityHours = emp.work_end - emp.work_start;
|
||||
|
||||
if (dateObj.isBefore(now, 'days')) {
|
||||
// Past date
|
||||
return '0';
|
||||
} else if (dateObj.isSame(now, 'days')) {
|
||||
// Today - calculate remaining capacity
|
||||
const elapsedMinutes = now.diff(now.clone().startOf('day'), 'minutes');
|
||||
const workStart =
|
||||
emp.work_start * 60 > elapsedMinutes
|
||||
? emp.work_start
|
||||
: elapsedMinutes / 60;
|
||||
const todayCapacityHours = emp.work_end - workStart;
|
||||
const free = todayCapacityHours - scheduled;
|
||||
return free < 0 ? '0' : free.toFixed(1);
|
||||
} else {
|
||||
// Future date
|
||||
const free = dailyCapacityHours - scheduled;
|
||||
return free < 0 ? '0' : free.toFixed(1);
|
||||
}
|
||||
return (dailyCapacityHours - scheduled).toFixed(1);
|
||||
});
|
||||
|
||||
// Detect collisions with current timeblock
|
||||
@@ -237,6 +202,22 @@ export function useEmployeeTimeline(
|
||||
return calculateTimeblockDuration(timeblock);
|
||||
});
|
||||
|
||||
watch(
|
||||
scheduledTime,
|
||||
(newVal) => {
|
||||
console.log('scheduledTime', newVal);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
freeTime,
|
||||
(newVal) => {
|
||||
console.log('freeTime', newVal);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
return {
|
||||
// Query states
|
||||
isLoading,
|
||||
|
||||
@@ -316,7 +316,7 @@ export default {
|
||||
},
|
||||
|
||||
taskClicked(subtask) {
|
||||
// console.log('task clicked')
|
||||
console.log('task clicked', subtask);
|
||||
},
|
||||
|
||||
eventFocused(event) {
|
||||
|
||||
@@ -150,6 +150,20 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
ends_at_time() {
|
||||
this._syncDatesTimes();
|
||||
},
|
||||
|
||||
starts_at_time() {
|
||||
this._syncDatesTimes();
|
||||
},
|
||||
|
||||
datepicker() {
|
||||
this._syncDatesTimes();
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async showModal({ blocker }) {
|
||||
this.visible = true;
|
||||
@@ -252,20 +266,6 @@ export default {
|
||||
.format('YYYY-MM-DD HH:mm');
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
ends_at_time() {
|
||||
this._syncDatesTimes();
|
||||
},
|
||||
|
||||
starts_at_time() {
|
||||
this._syncDatesTimes();
|
||||
},
|
||||
|
||||
datepicker() {
|
||||
this._syncDatesTimes();
|
||||
},
|
||||
},
|
||||
} /* c8 ignore start */ /* eslint-disable */;
|
||||
/* istanbul ignore next */
|
||||
function oo_cm() {
|
||||
|
||||
@@ -430,6 +430,57 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
editMode() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs['description']?.setHTML(this.mastertask.description || '');
|
||||
|
||||
if (!this.mastertask?.id) return;
|
||||
|
||||
const date = new Date(this.mastertask.deadline);
|
||||
const hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
|
||||
this.mastertask._deadline_time = `${hours
|
||||
.toString()
|
||||
.padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
|
||||
});
|
||||
},
|
||||
|
||||
mastertask: {
|
||||
handler(val) {
|
||||
if (this.editMode && !this.mastertask?.id) {
|
||||
localStorage.setItem('mastertaskData', JSON.stringify(val));
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
'mastertask.team_id': {
|
||||
handler(value, oldValue) {
|
||||
if (this.editMode && !value && oldValue) {
|
||||
// Removes only employees from the old team when team_id is removed
|
||||
const oldTeamEmployeesIds =
|
||||
this.teams
|
||||
.find((team) => team.id == oldValue)
|
||||
?.employees.map((employee) => employee?.id) || [];
|
||||
const employeesNotInOldTeam = this.mastertask.employees.filter(
|
||||
(employee) => !oldTeamEmployeesIds.includes(employee?.id),
|
||||
);
|
||||
this.mastertask.employees = employeesNotInOldTeam;
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
'$route.query': {
|
||||
handler(_, oldValue) {
|
||||
if (oldValue?.subtask?.length && this.mastertask?.id) {
|
||||
this.loadMastertask(this.mastertask.id);
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$events.on('close-both-modals', this.closeBothModals);
|
||||
|
||||
@@ -693,57 +744,6 @@ export default {
|
||||
return isValid;
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
editMode() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs['description']?.setHTML(this.mastertask.description || '');
|
||||
|
||||
if (!this.mastertask?.id) return;
|
||||
|
||||
const date = new Date(this.mastertask.deadline);
|
||||
const hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
|
||||
this.mastertask._deadline_time = `${hours
|
||||
.toString()
|
||||
.padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
|
||||
});
|
||||
},
|
||||
|
||||
mastertask: {
|
||||
handler(val) {
|
||||
if (this.editMode && !this.mastertask?.id) {
|
||||
localStorage.setItem('mastertaskData', JSON.stringify(val));
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
'mastertask.team_id': {
|
||||
handler(value, oldValue) {
|
||||
if (this.editMode && !value && oldValue) {
|
||||
// Removes only employees from the old team when team_id is removed
|
||||
const oldTeamEmployeesIds =
|
||||
this.teams
|
||||
.find((team) => team.id == oldValue)
|
||||
?.employees.map((employee) => employee?.id) || [];
|
||||
const employeesNotInOldTeam = this.mastertask.employees.filter(
|
||||
(employee) => !oldTeamEmployeesIds.includes(employee?.id),
|
||||
);
|
||||
this.mastertask.employees = employeesNotInOldTeam;
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
'$route.query': {
|
||||
handler(_, oldValue) {
|
||||
if (oldValue?.subtask?.length && this.mastertask?.id) {
|
||||
this.loadMastertask(this.mastertask.id);
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -392,7 +392,8 @@ export default {
|
||||
this._update(subtask);
|
||||
},
|
||||
|
||||
deleteTimeblockWithoutId(i) {
|
||||
deleteTimeblockWithoutId(index) {
|
||||
console.log(index);
|
||||
this.$emit('delete-timeblock');
|
||||
},
|
||||
|
||||
|
||||
@@ -15,25 +15,27 @@
|
||||
@update:model-value="handleUpdateEmployee"
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
:model-value="modelValue.duration"
|
||||
class="employee-select__duration-field"
|
||||
variant="solo"
|
||||
type="number"
|
||||
label="Časový odhad"
|
||||
:class="{ error: v$.duration.$error }"
|
||||
:error-messages="v$.duration.$errors.map((e: any) => String(e.$message))"
|
||||
append-inner-icon="mdi-clock-outline"
|
||||
@update:model-value="handleUpdateDuration"
|
||||
/>
|
||||
<div class="employee-select__duration-container">
|
||||
<v-text-field
|
||||
:model-value="modelValue.duration"
|
||||
class="employee-select__duration-field"
|
||||
variant="solo"
|
||||
type="number"
|
||||
label="Časový odhad"
|
||||
:class="{ error: v$.duration.$error }"
|
||||
:error-messages="v$.duration.$errors.map((e: any) => String(e.$message))"
|
||||
append-inner-icon="mdi-clock-outline"
|
||||
@update:model-value="handleUpdateDuration"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="employee-select__delete-button"
|
||||
:disabled="!modelValue.id"
|
||||
@click="handleClearEmployee"
|
||||
>
|
||||
<v-icon icon="mdi-close" />
|
||||
</button>
|
||||
<button
|
||||
class="employee-select__delete-button"
|
||||
:disabled="!modelValue.id"
|
||||
@click="handleClearEmployee"
|
||||
>
|
||||
<v-icon icon="mdi-close" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -160,6 +162,16 @@ defineExpose({
|
||||
align-items: start
|
||||
gap: 10px
|
||||
|
||||
@media (max-width: 768px)
|
||||
flex-direction: column
|
||||
gap: 5px
|
||||
|
||||
&__duration-container
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: start
|
||||
gap: 10px
|
||||
|
||||
&__select-field
|
||||
width: 100%
|
||||
|
||||
|
||||
@@ -73,6 +73,17 @@
|
||||
hide-details
|
||||
@input="$emit('updateEndTime', $event)"
|
||||
/>
|
||||
|
||||
<div class="timeblock__time-overview">
|
||||
<p>
|
||||
Naplánovaný čas:
|
||||
<span>{{ plannedTime }}h</span>
|
||||
</p>
|
||||
<p>
|
||||
Volný čas:
|
||||
<span>{{ freeTime }}h</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isTimeRangeValid || endTimeErrorMessage"
|
||||
@@ -115,10 +126,14 @@ interface Props {
|
||||
hasStartTimeError: boolean;
|
||||
hasEndTimeError: boolean;
|
||||
endTimeErrorMessage?: string;
|
||||
plannedTime: string | number;
|
||||
freeTime: string | number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
endTimeErrorMessage: '',
|
||||
plannedTime: '',
|
||||
freeTime: '',
|
||||
});
|
||||
|
||||
defineEmits<{
|
||||
@@ -231,6 +246,20 @@ const isCurrentDay = computed(() => {
|
||||
width: 140px !important
|
||||
flex-grow: 0
|
||||
|
||||
.timeblock__time-overview
|
||||
height: 44px
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: space-between
|
||||
|
||||
p
|
||||
font-size: 14px
|
||||
font-weight: 400
|
||||
|
||||
span
|
||||
font-weight: 500
|
||||
color: #7f39fb
|
||||
|
||||
.error-input
|
||||
::v-deep input
|
||||
border-bottom: 2px solid #ff5252 !important
|
||||
|
||||
+3
-3
@@ -180,13 +180,13 @@ const getTimePosition = (date: Date): number => {
|
||||
const hour = date.getHours();
|
||||
const minute = date.getMinutes();
|
||||
const totalHours = hour + minute / 60;
|
||||
return (totalHours - props.startHour) * props.hourWidth;
|
||||
return (totalHours - props.startHour) * props.hourWidth + 1;
|
||||
};
|
||||
|
||||
const getTimeWidth = (start: Date, end: Date): number => {
|
||||
const startHour = start.getHours() + start.getMinutes() / 60;
|
||||
const endHour = end.getHours() + end.getMinutes() / 60;
|
||||
return (endHour - startHour) * props.hourWidth;
|
||||
return (endHour - startHour) * props.hourWidth - 2;
|
||||
};
|
||||
|
||||
// Expose refs for parent component
|
||||
@@ -252,6 +252,7 @@ defineExpose({
|
||||
top: 5px
|
||||
height: 50px
|
||||
background: #7f39fb
|
||||
border: 1px solid #6a1bf7
|
||||
opacity: 0.6
|
||||
border-radius: 2px
|
||||
z-index: 2
|
||||
@@ -299,4 +300,3 @@ defineExpose({
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3)
|
||||
pointer-events: none
|
||||
</style>
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
:has-start-time-error="hasStartTimeError"
|
||||
:has-end-time-error="hasEndTimeError"
|
||||
:end-time-error-message="endTimeErrorMessage"
|
||||
:planned-time="_scheduledTime"
|
||||
:free-time="_freeTime"
|
||||
@previous-day="previousDay"
|
||||
@next-day="nextDay"
|
||||
@update-date="updateDate"
|
||||
|
||||
@@ -143,6 +143,19 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
employeeSelect(employeeId) {
|
||||
if (!employeeId) return;
|
||||
|
||||
const employee = this.enrichedEmployees.find(
|
||||
(employee) => employee.id == employeeId,
|
||||
);
|
||||
this.team.employees.push(employee);
|
||||
this.employeeSelect = null;
|
||||
this.$refs.autocomplete.search = '';
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async showModal({ team = null }) {
|
||||
this.visible = true;
|
||||
@@ -200,18 +213,5 @@ export default {
|
||||
return team;
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
employeeSelect(employeeId) {
|
||||
if (!employeeId) return;
|
||||
|
||||
const employee = this.enrichedEmployees.find(
|
||||
(employee) => employee.id == employeeId,
|
||||
);
|
||||
this.team.employees.push(employee);
|
||||
this.employeeSelect = null;
|
||||
this.$refs.autocomplete.search = '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -80,6 +80,12 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
user() {
|
||||
this._setDates();
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this._setDates();
|
||||
},
|
||||
@@ -122,20 +128,14 @@ export default {
|
||||
// },
|
||||
|
||||
decimalHoursToTime(decimalHours) {
|
||||
var hours = Math.floor(decimalHours);
|
||||
var mins = Math.floor((decimalHours * 60) % 60);
|
||||
let hours = Math.floor(decimalHours);
|
||||
let mins = Math.floor((decimalHours * 60) % 60);
|
||||
hours = (hours < 10 ? '0' : '') + hours;
|
||||
mins = (mins < 10 ? '0' : '') + mins;
|
||||
|
||||
return hours + ':' + mins;
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
user() {
|
||||
this._setDates();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
Reference in new issue
Block a user