This commit is contained in:
romankko2 committed 2023-01-11 12:17:56 +01:00
commit 8d49ad7122
10 files changed
+457 -149

No files matched your search

+38 -28
View File
@@ -39,92 +39,102 @@
{ {
"id": 1, "id": 1,
"task_id": 1, "task_id": 1,
"starts_at": "2023-01-03T08:01:00+00:00", "starts_at": "2023-01-08T08:01:00+00:00",
"ends_at": "2023-01-03T09:00:00+00:00", "ends_at": "2023-01-08T09:00:00+00:00",
"title": "Task 1", "title": "Task DNES",
"project_code": "CO-2201", "project_code": "CO-2201",
"master_task_color": "#E9FFE6" "master_task_color": "#E9FFE6",
"completed": false
}, },
{ {
"id": 2, "id": 2,
"task_id": 2, "task_id": 2,
"starts_at": "2023-01-03T08:30:00+00:00", "starts_at": "2023-01-08T08:30:00+00:00",
"ends_at": "2023-01-03T09:00:00+00:00", "ends_at": "2023-01-08T09:00:00+00:00",
"title": "Kratky 30min Lorem ipsum dolor sit amet, consectetur adipiscing e", "title": "Kratky 30min Lorem ipsum dolor sit amet, consectetur adipiscing e",
"project_code": "CO-2201", "project_code": "CO-2201",
"master_task_color": "#f27e57" "master_task_color": "#f27e57",
"completed": false
}, },
{ {
"id": 3, "id": 3,
"task_id": 3, "task_id": 3,
"starts_at": "2023-01-03T10:30:00+00:00", "starts_at": "2023-01-08T10:30:00+00:00",
"ends_at": "2023-01-03T10:45:00+00:00", "ends_at": "2023-01-08T10:45:00+00:00",
"title": "Kratky 15min", "title": "Kratky 15min",
"project_code": "CO-2201", "project_code": "CO-2201",
"master_task_color": "#57e0f2" "master_task_color": "#57e0f2",
"completed": false
}, },
{ {
"id": 4, "id": 4,
"task_id": 4, "task_id": 4,
"starts_at": "2023-01-03T08:00:00+00:00", "starts_at": "2023-01-08T08:00:00+00:00",
"ends_at": "2023-01-03T10:00:00+00:00", "ends_at": "2023-01-08T10:00:00+00:00",
"title": "Task 2", "title": "Task 2",
"project_code": "CO-2201", "project_code": "CO-2201",
"master_task_color": "#FDE1FF" "master_task_color": "#FDE1FF",
"completed": false
}, },
{ {
"id": 6, "id": 6,
"task_id": 6, "task_id": 6,
"starts_at": "2023-01-04T09:00:00+00:00", "starts_at": "2023-01-09T09:00:00+00:00",
"ends_at": "2023-01-04T12:00:00+00:00", "ends_at": "2023-01-09T12:00:00+00:00",
"title": "Task 6", "title": "Task 6",
"project_code": "CO-2201", "project_code": "CO-2201",
"master_task_color": "#57e0f2" "master_task_color": "#57e0f2",
"completed": false
}, },
{ {
"id": 7, "id": 7,
"task_id": 7, "task_id": 7,
"starts_at": "2023-01-05T12:00:00+00:00", "starts_at": "2023-01-10T12:00:00+00:00",
"ends_at": "2023-01-05T16:00:00+00:00", "ends_at": "2023-01-10T16:00:00+00:00",
"title": "Task 7", "title": "Task 7",
"project_code": "CO-2201", "project_code": "CO-2201",
"master_task_color": "#E7DBF8" "master_task_color": "#E7DBF8",
"completed": true
}, },
{ {
"id": 8, "id": 8,
"task_id": 8, "task_id": 8,
"starts_at": "2023-01-03T07:00:00+00:00", "starts_at": "2023-01-08T07:00:00+00:00",
"ends_at": "2023-01-03T09:00:00+00:00", "ends_at": "2023-01-08T09:00:00+00:00",
"title": "Task 8", "title": "Task 8",
"project_code": "CO-2201", "project_code": "CO-2201",
"master_task_color": "#57e0f2" "master_task_color": "#57e0f2",
"completed": false
}, },
{ {
"id": 9, "id": 9,
"task_id": 9, "task_id": 9,
"starts_at": "2023-01-04T09:00:00+00:00", "starts_at": "2023-01-09T09:00:00+00:00",
"ends_at": "2023-01-04T12:00:00+00:00", "ends_at": "2023-01-09T12:00:00+00:00",
"title": "Task 9", "title": "Task 9",
"project_code": "CO-2201", "project_code": "CO-2201",
"master_task_color": "#E9FFE6" "master_task_color": "#E9FFE6",
"completed": false
}, },
{ {
"id": 10, "id": 10,
"task_id": 10, "task_id": 10,
"starts_at": "2023-01-03T12:00:00+00:00", "starts_at": "2023-01-08T12:00:00+00:00",
"ends_at": "2023-01-03T13:00:00+00:00", "ends_at": "2023-01-08T13:00:00+00:00",
"title": "Task 10", "title": "Task 10",
"project_code": "CO-2201", "project_code": "CO-2201",
"master_task_color": "#57e0f2" "master_task_color": "#57e0f2",
"completed": false
} }
] ]
}, },
"status": 200 "status": 200
+148 -55
View File
@@ -1,80 +1,173 @@
{ {
"data": { "data": [
"unscheduled_time": 123123123,
"users_tasks": [
{ {
"user": {
"id": 1, "id": 1,
"name": "Rastislav", "name": "Rastislav",
"surname": "Kuttner", "surname": "Kuttner",
"work_hours_from": "08:00", "work_hours_from": "08:00",
"work_hours_to": "16:00" "work_hours_to": "19:00",
}, "unscheduled_time": 10,
"tasks": [ "tasks": [
{ {
"id": 1, "id": 1,
"is_finished": false, "task_id": 1,
"starts_at": "2022-11-10T09:00:00+00:00", "starts_at": "2023-01-11T08:01:00+00:00",
"ends_at": "2022-11-12T12:00:00+00:00", "ends_at": "2023-01-11T09:00:00+00:00",
"files": [] "title": "Task DNES",
"project_code": "CO-2201",
"master_task_color": "#E9FFE6",
"completed": false
}, },
{ {
"id": 2, "id": 2,
"is_finished": false, "task_id": 2,
"starts_at": "2022-11-10T12:00:00+00:00", "starts_at": "2023-01-11T08:30:00+00:00",
"ends_at": "2022-11-12T16:00:00+00:00", "ends_at": "2023-01-11T09:00:00+00:00",
"files": [] "title": "Kratky 30min Lorem ipsum dolor sit amet, consectetur adipiscing e",
"project_code": "CO-2201",
"master_task_color": "#f27e57",
"completed": false
}, },
{
"id": 4,
"is_finished": false,
"starts_at": "2022-11-10T16:00:00+00:00",
"ends_at": "2022-11-12T18:00:00+00:00",
"is_blocker": true
},
{
"id": 4,
"is_finished": false,
"starts_at": "2022-12-12T17:30:00+00:00",
"ends_at": "2022-12-12T18:00:00+00:00",
"is_blocker": true
}
]
},
{
"user": {
"id": 2,
"name": "Lenka",
"surname": "Nemeckova",
"work_hours_from": "09:00",
"work_hours_to": "17:00"
},
"tasks": [
{ {
"id": 3, "id": 3,
"is_finished": false, "task_id": 3,
"starts_at": "2022-11-10T09:00:00+00:00", "starts_at": "2023-01-11T10:30:00+00:00",
"ends_at": "2022-11-12T12:00:00+00:00", "ends_at": "2023-01-11T10:45:00+00:00",
"files": [] "title": "Kratky 15min",
"project_code": "CO-2201",
"master_task_color": "#57e0f2",
"completed": false
}
]
},
{
"id": 2,
"name": "Rastislav",
"surname": "Kuttner",
"work_hours_from": "05:00",
"work_hours_to": "16:00",
"unscheduled_time": 10,
"tasks": [
{
"id": 1,
"task_id": 1,
"starts_at": "2023-01-11T08:01:00+00:00",
"ends_at": "2023-01-11T09:00:00+00:00",
"title": "Task DNES",
"project_code": "CO-2201",
"master_task_color": "#E9FFE6",
"completed": false
}, },
{ {
"id": 4, "id": 2,
"is_finished": false, "task_id": 2,
"starts_at": "2022-11-10T12:00:00+00:00", "starts_at": "2023-01-11T08:30:00+00:00",
"ends_at": "2022-11-12T16:00:00+00:00", "ends_at": "2023-01-11T09:00:00+00:00",
"files": [] "title": "Kratky 30min Lorem ipsum dolor sit amet, consectetur adipiscing e",
} "project_code": "CO-2201",
] "master_task_color": "#f27e57",
"completed": false
},
{
"id": 3,
"task_id": 3,
"starts_at": "2023-01-11T10:30:00+00:00",
"ends_at": "2023-01-11T10:45:00+00:00",
"title": "Kratky 15min",
"project_code": "CO-2201",
"master_task_color": "#57e0f2",
"completed": false
} }
] ]
}, },
{
"id": 3,
"name": "Rastislav",
"surname": "Kuttner",
"work_hours_from": "07:30",
"work_hours_to": "18:30",
"unscheduled_time": 10,
"tasks": [
{
"id": 1,
"task_id": 1,
"starts_at": "2023-01-11T08:01:00+00:00",
"ends_at": "2023-01-11T09:00:00+00:00",
"title": "Task DNES",
"project_code": "CO-2201",
"master_task_color": "#E9FFE6",
"completed": false
},
{
"id": 2,
"task_id": 2,
"starts_at": "2023-01-11T08:30:00+00:00",
"ends_at": "2023-01-11T09:00:00+00:00",
"title": "Kratky 30min Lorem ipsum dolor sit amet, consectetur adipiscing e",
"project_code": "CO-2201",
"master_task_color": "#f27e57",
"completed": false
},
{
"id": 3,
"task_id": 3,
"starts_at": "2023-01-11T10:30:00+00:00",
"ends_at": "2023-01-11T10:45:00+00:00",
"title": "Kratky 15min",
"project_code": "CO-2201",
"master_task_color": "#57e0f2",
"completed": false
}
]
},
{
"id": 4,
"name": "Rastislav",
"surname": "Kuttner",
"work_hours_from": "07:00",
"work_hours_to": "18:00",
"unscheduled_time": 10,
"tasks": [
{
"id": 1,
"task_id": 1,
"starts_at": "2023-01-11T08:01:00+00:00",
"ends_at": "2023-01-11T09:00:00+00:00",
"title": "Task DNES",
"project_code": "CO-2201",
"master_task_color": "#E9FFE6",
"completed": false
},
{
"id": 2,
"task_id": 2,
"starts_at": "2023-01-11T08:30:00+00:00",
"ends_at": "2023-01-11T09:00:00+00:00",
"title": "Kratky 30min Lorem ipsum dolor sit amet, consectetur adipiscing e",
"project_code": "CO-2201",
"master_task_color": "#f27e57",
"completed": false
},
{
"id": 3,
"task_id": 3,
"starts_at": "2023-01-11T10:30:00+00:00",
"ends_at": "2023-01-11T10:45:00+00:00",
"title": "Kratky 15min",
"project_code": "CO-2201",
"master_task_color": "#57e0f2",
"completed": false
}
]
}
],
"status": 200 "status": 200
} }
+1 -2
View File
@@ -157,9 +157,8 @@ export default {
}, },
onDragStart(e) { onDragStart(e) {
console.log(`ID from dragStart: ${this.event.id}`) if (this.event.nonWorking) return
this.event.draggingStatic = false this.event.draggingStatic = false
this.dnd && this.dnd.eventDragStart(e, this.event); this.dnd && this.dnd.eventDragStart(e, this.event);
}, },
+4 -3
View File
@@ -16,7 +16,7 @@
<template #weekday-heading="{ heading, view }" v-if="$slots['weekday-heading']"> <template #weekday-heading="{ heading, view }" v-if="$slots['weekday-heading']">
<slot name="weekday-heading" :heading="heading" :view="view"></slot> <slot name="weekday-heading" :heading="heading" :view="view"></slot>
</template> </template>
<template #split-label="{ split }" v-if="$slots['split-label']"> <template #split-label="{ split, view }" v-if="$slots['split-label']">
<slot name="split-label" :split="split" :view="view.id"></slot> <slot name="split-label" :split="split" :view="view.id"></slot>
</template> </template>
</vuecal-header> </vuecal-header>
@@ -58,7 +58,7 @@
<slot name="split-label" :split="split" :view="view.id"></slot> <slot name="split-label" :split="split" :view="view.id"></slot>
</template> </template>
</weekdays-headings> </weekdays-headings>
<div class="vuecal__flex vuecal__split-days-headers" v-else-if="hasSplits &amp;&amp; stickySplitLabels &amp;&amp; minSplitWidth" :style="cellOrSplitMinWidth ? `min-width: ${cellOrSplitMinWidth}px` : ''"> <div class="vuecal__flex vuecal__split-days-headers" ref="vuecal_splits_heading" v-else-if="hasSplits &amp;&amp; stickySplitLabels &amp;&amp; minSplitWidth" :style="cellOrSplitMinWidth ? `min-width: ${cellOrSplitMinWidth}px` : ''">
<div class="day-split-header" v-for="(split, i) in daySplits" :key="i" :class="split.class || false"> <div class="day-split-header" v-for="(split, i) in daySplits" :key="i" :class="split.class || false">
<slot name="split-label" :split="split" :view="view.id">{{ split.label }}</slot> <slot name="split-label" :split="split" :view="view.id">{{ split.label }}</slot>
</div> </div>
@@ -1307,7 +1307,8 @@ export default {
} }
this.$refs['vuecal_scroll'].addEventListener('scroll', () => { this.$refs['vuecal_scroll'].addEventListener('scroll', () => {
this.$refs['vuecal_weekday_heading'].onTimelineScroll(this.$refs['vuecal_scroll'].scrollTop) // this.$refs['vuecal_splits_heading'].onTimelineScroll(this.$refs['vuecal_scroll'].scrollTop)
this.$refs['vuecal_splits_heading'].style.top = `${this.$refs['vuecal_scroll'].scrollTop}px`
}) })
this.viewportTimelineOffset = this.$refs['vuecal_scroll'].getBoundingClientRect().top this.viewportTimelineOffset = this.$refs['vuecal_scroll'].getBoundingClientRect().top
@@ -47,8 +47,8 @@ export default {
methods: { methods: {
getWeek() { getWeek() {
this.firstDay = addDays(startOfWeek(this.date), 1) this.firstDay = subDays(this.date, 1)
this.lastDay = addDays(endOfWeek(this.date), 1) this.lastDay = addDays(this.date, 5)
this.$emit('weekChange', {firstDay: this.firstDay, lastDay: this.lastDay}) this.$emit('weekChange', {firstDay: this.firstDay, lastDay: this.lastDay})
this.selectedDate = String(this.firstDay.toLocaleDateString('sk-SK') + ' - ' + this.lastDay.toLocaleDateString('sk-SK')) this.selectedDate = String(this.firstDay.toLocaleDateString('sk-SK') + ' - ' + this.lastDay.toLocaleDateString('sk-SK'))
@@ -3,7 +3,8 @@
<Vue-cal <Vue-cal
:hide-view-selector="true" :hide-view-selector="true"
:hide-title-bar="true" :hide-title-bar="true"
:disable-views="['years', 'year', 'month', 'day']" :disable-views="['years', 'year', 'month', 'week']"
active-view="day"
:selected-date="calConfig.date" :selected-date="calConfig.date"
:onEventClick="taskClicked" :onEventClick="taskClicked"
:editable-events="vueCalConfig.editing" :editable-events="vueCalConfig.editing"
@@ -12,28 +13,33 @@
:overlaps-per-time-step="false" :overlaps-per-time-step="false"
:showTimeInCells="false" :showTimeInCells="false"
:time-cell-height="timeCellHeight" :time-cell-height="timeCellHeight"
:snap-to-time="8 * 60" :snap-to-time="15"
:scroll-to-hour="calConfig.scrollHour" :scroll-to-hour="calConfig.scrollHour"
:min-cell-width="300" :split-days="splits"
:sticky-split-labels="true"
:min-split-width="300"
@ready="onReady()" @ready="onReady()"
@view-change="weekChanged" @view-change="weekChanged"
@event-drop="eventMoved" @event-drop="eventMoved"
@event-focus="eventFocused" @event-focus="eventFocused"
> >
<template v-slot:weekday-heading="{ heading }"> <template v-slot:split-label="{ split }">
<div class="w-100 h-100 d-flex flex-column"> <div class="w-100 h-100 d-flex flex-column">
<span class="d-flex align-center"> <span v-if="split.date" class="d-flex align-center">
<img v-if="getHeadingDate(heading.date) == 'Dnes'" :src="require('./_img/home-icon.svg')" class="mr-2"> <img v-if="getHeadingDate(split.date) == 'Dnes'" :src="require('./_img/home-icon.svg')" class="mr-2">
<p>{{ getHeadingDate(heading.date) }}</p> <p class="weekday-label">{{ getHeadingDate(split.date) }}</p>
</span>
<span v-else class="d-flex align-center">
<p class="weekday-label">{{ split.firstName }}</p>
</span> </span>
<div v-if="heading.unscheduledTime && heading.initials" class="unscheduled-time d-flex align-center text-left"> <div v-if="split.unscheduledTime && split.initials" class="unscheduled-time d-flex align-center text-left">
<div class="unscheduled-time-initials d-flex align-center justify-center me-4"> <div class="unscheduled-time-initials d-flex align-center justify-center me-4">
<p>{{heading.initials}}</p> <p>{{split.initials}}</p>
</div> </div>
<div> <div>
<p class="mb-0 mt-0 unscheduled-time-name">{{heading.name}}</p> <p class="mb-0 mt-0 unscheduled-time-name">{{split.name}}</p>
<h1 class="mb-0 mt-0">{{heading.unscheduledTime}} h</h1> <h1 class="mb-0 mt-0">{{split.unscheduledTime}} h</h1>
</div> </div>
</div> </div>
</div> </div>
@@ -47,7 +53,7 @@
</template> </template>
<template v-slot:event="{ event }"> <template v-slot:event="{ event }">
<div @click.stop="editTask(event.id)" class="event-wrapper w-100 h-100 d-flex flex-column justify-space-between" :class="{'short-event' : getEventLenght(event) <= 9000000, 'mini-event' : getEventLenght(event) <= 5400000, 'micro-event' : getEventLenght(event) <= 3600000}"> <div v-if="!event.nonWorking" @click.stop="editTask(event.id)" class="w-100 h-100 d-flex flex-column justify-space-between" :class="{'short-event' : getEventLenght(event) <= 9000000, 'mini-event' : getEventLenght(event) <= 5400000, 'micro-event' : getEventLenght(event) <= 3600000}">
<div class="event-content"> <div class="event-content">
<small v-if="(event.project_code && !event.is_blocker)" class="project-code">{{ event.project_code }}</small> <small v-if="(event.project_code && !event.is_blocker)" class="project-code">{{ event.project_code }}</small>
<h3>{{ event.title }}</h3> <h3>{{ event.title }}</h3>
@@ -55,22 +61,29 @@
<div class="event-initials d-flex justify-center align-center"> <div class="event-initials d-flex justify-center align-center">
<small>{{ getUserInitials(event) }}</small> <small>{{ getUserInitials(event) }}</small>
</div> </div>
<small class="event-initials-date">{{ event.start.toLocaleDateString('sk-SK') }}</small> <small class="event-initials-date">{{ new Date(event.starts_at).toLocaleDateString('sk-SK') }}</small>
</div> </div>
</div> </div>
<div class="event-footer d-flex"> <div class="event-footer d-flex">
<v-btn rounded class="event-footer-button me-2" @click.stop="this.showCompleteModal = {task: event}"><img :src="require('./_img/checkmark.svg')" alt=""></v-btn> <v-btn rounded class="event-footer-button me-2" :class="{'completed': event.completed}" @click="this.showCompleteModal = {task: event}"><img :src="require('./_img/checkmark.svg')" alt=""></v-btn>
<v-btn rounded class="event-footer-button me-2" @click.stop="editTask(event.id, true)"><img :src="require('./_img/pencil.svg')" alt=""></v-btn> <v-btn rounded class="event-footer-button me-2" @click.stop="editTask(event.id, true)"><img :src="require('./_img/pencil.svg')" alt=""></v-btn>
<v-btn rounded class="event-footer-button me-2" @click.stop="this.showDeleteModal = {task: event}"><img :src="require('./_img/bin.svg')" alt=""></v-btn> <v-btn rounded class="event-footer-button me-2" @click.stop="this.showDeleteModal = {task: event}"><img :src="require('./_img/bin.svg')" alt=""></v-btn>
</div> </div>
</div> </div>
<div v-else class="non-working-event w-100 h-100 d-flex flex-column justify-center">
<div class="d-flex justify-center align-center">
<v-icon icon="mdi-cancel" color="black"></v-icon>
<h3 class="mb-0 ms-2">{{ event.title }}</h3>
</div>
</div>
</template> </template>
</Vue-cal> </Vue-cal>
<Z-sub-task-modal ref="z-sub-task-modal" /> <Z-sub-task-modal ref="z-sub-task-modal" />
<Z-complete-task-modal v-if="showCompleteModal" :visible="showCompleteModal ? true : false" :task="showCompleteModal.task" @close="showCompleteModal = null" /> <Z-complete-task-modal v-if="showCompleteModal" :visible="showCompleteModal ? true : false" :task="showCompleteModal.task" @completeTask="completeTask" @close="showCompleteModal = null" />
<Z-delete-task-modal v-if="showDeleteModal" :visible="showDeleteModal ? true : false" :task="showDeleteModal.task" @close="showDeleteModal = null" /> <Z-delete-task-modal v-if="showDeleteModal" :visible="showDeleteModal ? true : false" :task="showDeleteModal.task" @deleteTask="deleteTask" @close="showDeleteModal = null" />
</div> </div>
</template> </template>
@@ -100,6 +113,13 @@ export default {
default() { default() {
return {date: new Date(), scrollHour: 7} return {date: new Date(), scrollHour: 7}
} }
},
splits: {
type: Array,
required: false,
default() {
return []
}
} }
}, },
@@ -122,17 +142,13 @@ export default {
}, },
mounted() { mounted() {
this._scrollToDefaultTime() // console.log(this.calData.tasks)
}, },
methods: { methods: {
weekChanged() { weekChanged() {
const scrollContainer = document.getElementsByClassName('vuecal__scrollable-body')[0] const scrollContainer = document.getElementsByClassName('vuecal__scrollable-body')[0]
scrollContainer.scrollLeft = 0 scrollContainer.scrollLeft = 0
this.$nextTick(() => {
this.scrollToYesterday()
})
}, },
async eventMoved(e) { async eventMoved(e) {
@@ -141,20 +157,12 @@ export default {
}, },
onReady() { onReady() {
this.scrollToYesterday()
this.correctMargins() this.correctMargins()
}, },
scrollToYesterday() {
if (!document.getElementsByClassName('yesterday-cell').length || !document.getElementsByClassName('today-cell').length) return
const scrollContainer = document.getElementsByClassName('vuecal__scrollable-body')[0]
const yesterdayCell = document.getElementsByClassName('yesterday-cell')[0]
scrollContainer.scrollLeft = yesterdayCell.offsetLeft - scrollContainer.offsetLeft - 20
},
correctMargins() { correctMargins() {
const heightOfDayHeader = document.getElementsByClassName('vuecal__heading')[0].clientHeight if (!document.getElementsByClassName('vuecal__split-days-headers').length) return
const heightOfDayHeader = document.getElementsByClassName('vuecal__split-days-headers')[0].clientHeight
document.getElementsByClassName('vuecal__time-column')[0].style.marginTop = `${heightOfDayHeader}px` document.getElementsByClassName('vuecal__time-column')[0].style.marginTop = `${heightOfDayHeader}px`
document.getElementsByClassName('vuecal__cells-body')[0].style.marginTop = `${heightOfDayHeader}px` document.getElementsByClassName('vuecal__cells-body')[0].style.marginTop = `${heightOfDayHeader}px`
}, },
@@ -182,6 +190,7 @@ export default {
}, },
taskClicked(task) { taskClicked(task) {
if (task.nonWorking) return
console.log("task", task) console.log("task", task)
if (task.end.getTime() - task.start.getTime() <= 3600000) { if (task.end.getTime() - task.start.getTime() <= 3600000) {
console.log("ShortTask") console.log("ShortTask")
@@ -190,22 +199,19 @@ export default {
eventFocused(event) { eventFocused(event) {
this.focusedEventId = event.task_id this.focusedEventId = event.task_id
console.log(`Focused: ${event.task_id}`) // console.log(`Focused: ${event.task_id}`)
}, },
_scrollToDefaultTime() { completeTask(id) {
const calendarContainer = document.querySelector('.main-content') this.$emit('completeTask', id)
const defaultTimeToScrollTo = document.querySelectorAll('.vuecal__time-cell')[7]
const toolbarHeight = document.querySelector('.v-toolbar').getBoundingClientRect().height
const subheaderHeight = document.querySelector('.subheader').getBoundingClientRect().height
calendarContainer.scrollTo({
top: defaultTimeToScrollTo.getBoundingClientRect().top - toolbarHeight - subheaderHeight
})
}, },
editTask(subTask, editMode) { editTask(subTask, editMode) {
this.$refs['z-sub-task-modal'].create(subTask, editMode) this.$refs['z-sub-task-modal'].create(subTask, editMode)
},
deleteTask(id) {
this.$emit('deleteTask', id)
} }
} }
} }
@@ -218,7 +224,7 @@ export default {
.calendar-wrapper .calendar-wrapper
width: 100% width: 100%
height: 100% height: 100%
// padding-top: 50px // padding: 0 20px 0 0
&:deep() &:deep()
.vuecal__cells .vuecal__cells
@@ -227,7 +233,6 @@ export default {
&::-webkit-scrollbar &::-webkit-scrollbar
display: none display: none
&.week-view
border-left: 1px solid rgba(0, 0, 0, .1) border-left: 1px solid rgba(0, 0, 0, .1)
padding-left: 20px padding-left: 20px
@@ -255,21 +260,27 @@ export default {
// HEADING // HEADING
.vuecal__weekdays-headings, // .vuecal__weekdays-headings,
.vuecal__cell:before .vuecal__cell:before
border: none border: none
.vuecal__scrollable-body .vuecal__scrollable-body
position: relative position: relative
.vuecal__weekdays-headings // .vuecal__weekdays-headings
.vuecal__split-days-headers
position: absolute position: absolute
top: 0 top: 0
left: 20px left: 20px
z-index: 999 z-index: 999
background: white background: white
padding-bottom: 15px
width: calc( 100% - 20px )
.vuecal__heading .vuecal__cell-split
margin-right: 20px
.day-split-header
height: fit-content height: fit-content
min-height: 100% min-height: 100%
padding-right: 20px padding-right: 20px
@@ -311,8 +322,8 @@ export default {
.vuecal__now-line .vuecal__now-line
display: none !important display: none !important
.vuecal__cell // .vuecal__cell
padding: 10px 20px 10px 0px // padding: 10px 20px 10px 0px
.vuecal:not(.vuecal--day-view) .vuecal__cell--selected:not(.vuecal__cell--today) , .vuecal:not(.vuecal--day-view) .vuecal__cell--selected:not(.vuecal__cell--today) ,
.vuecal__cell--current:not(.vuecal__cell--today), .vuecal__cell--current:not(.vuecal__cell--today),
@@ -322,6 +333,7 @@ export default {
background-color: white background-color: white
// EVENT // EVENT
.vuecal__event .vuecal__event
border-radius: 16px border-radius: 16px
padding: 20px 10px padding: 20px 10px
@@ -330,6 +342,13 @@ export default {
min-height: 40px min-height: 40px
transition: min-height .4s, height .4s, box-shadow .4s, width .4s, left .4s transition: min-height .4s, height .4s, box-shadow .4s, width .4s, left .4s
// &:has(.completed)
// filter: grayscale(.9) !important
&:has(.non-working-event)
background: repeating-linear-gradient(135deg, rgba(215, 215, 215, 1), rgba(215, 215, 215, 1) 1px, rgba(224, 224, 224, 1) 1px, rgba(224, 224, 224, 1) 20px)
border: 1px solid rgba(215, 215, 215, 1)
&.vuecal__event--focus &.vuecal__event--focus
z-index: 100 !important z-index: 100 !important
min-height: 250px !important min-height: 250px !important
@@ -432,6 +451,9 @@ export default {
min-width: fit-content min-width: fit-content
width: fit-content width: fit-content
&.completed
background: #00B3A6
img img
width: 18px width: 18px
</style> </style>
+75 -9
View File
@@ -3,7 +3,7 @@
<A-task-filter @weekChange="getWeekData" /> <A-task-filter @weekChange="getWeekData" />
<W-loadable :content="timelineData" class="w-loadable" empty-message="Nepodarilo sa nacitat data!"> <W-loadable :content="timelineData" class="w-loadable" empty-message="Nepodarilo sa nacitat data!">
<Z-timeline :calData="timelineData" :calConfig="config" @eventMoved="onEventMoved" /> <Z-timeline :calData="timelineData" :calConfig="config" :splits="splitDays" @eventMoved="onEventMoved" @completeTask="completeTask" @deleteTask="deleteTask" />
</W-loadable> </W-loadable>
<v-snackbar <v-snackbar
@@ -21,6 +21,8 @@
import ATaskFilter from './_components/a-tasks-filter.vue' import ATaskFilter from './_components/a-tasks-filter.vue'
import ZTimeline from './_components/z-timeline.vue' import ZTimeline from './_components/z-timeline.vue'
import { addDays, setHours, setMinutes, setSeconds, differenceInCalendarDays } from 'date-fns'
export default { export default {
components: { components: {
ATaskFilter, ATaskFilter,
@@ -33,6 +35,7 @@ export default {
errorToast: false, errorToast: false,
errorToastMessage: "Nepodarilo sa nacitat data!", errorToastMessage: "Nepodarilo sa nacitat data!",
dataLoaded: false, dataLoaded: false,
splitDays: [],
config: { config: {
date: new Date(), date: new Date(),
scrollHour: 7, scrollHour: 7,
@@ -45,7 +48,9 @@ export default {
this.config.date = week.firstDay this.config.date = week.firstDay
try { try {
this.timelineData = await this.$axios.get_auth_data('v1/overview/tasks') this.timelineData = await this.$axios.get_auth_data('v1/overview/tasks')
this.formatData() this.formatData(week.firstDay)
this.generateSplits(week.firstDay)
this.generateNonWorkingHours(week.firstDay)
} catch(error) { } catch(error) {
this.timelineData = {} this.timelineData = {}
console.error(error) console.error(error)
@@ -53,15 +58,33 @@ export default {
} }
}, },
formatData() { generateSplits(date) {
this.splitDays = []
for (let i = 0; i < 7; i++) {
this.splitDays.push({
id: i + 1,
name: this.timelineData.name + " " + this.timelineData.surname,
initials: this.timelineData.name.charAt(0).toUpperCase() + this.timelineData.surname.charAt(0).toUpperCase(),
unscheduledTime: Math.floor(this.timelineData.unscheduled_times[i].time / 60) + "," + String(this.timelineData.unscheduled_times[i].time % 60)[0],
date: addDays(date, i)
})
}
},
formatData(date) {
// format events // format events
this.timelineData.tasks.forEach(event => { this.timelineData.tasks.forEach(event => {
event.start = new Date(event.starts_at)
event.end = new Date(event.ends_at)
event.name = this.timelineData.name event.name = this.timelineData.name
event.surname = this.timelineData.surname event.surname = this.timelineData.surname
delete event.starts_at
delete event.ends_at // convert all dates to one to show in day-splits
const startDate = new Date(event.starts_at)
const endDate = new Date(event.ends_at)
const diffInDays = differenceInCalendarDays(date, startDate)
event.start = addDays(startDate, diffInDays)
event.end = addDays(endDate, diffInDays)
event.split = diffInDays * (-1) + 1
}) })
this.timelineData.unscheduled_times.forEach(time => { this.timelineData.unscheduled_times.forEach(time => {
@@ -72,15 +95,46 @@ export default {
this.config.scrollHour = Number(this.timelineData.work_hours_from.split(':')[0]) - 1 this.config.scrollHour = Number(this.timelineData.work_hours_from.split(':')[0]) - 1
}, },
generateNonWorkingHours(date) {
const nonWorkingHours = []
const start1 = { hour: 0, minutes: 0, seconds: 0}
const end1 = { hour: Number(this.timelineData.work_hours_from.split(':')[0]), minutes: Number(this.timelineData.work_hours_from.split(':')[1]) - 1, seconds: 0 }
const start2 = { hour: Number(this.timelineData.work_hours_to.split(':')[0]), minutes: Number(this.timelineData.work_hours_to.split(':')[1]) + 1, seconds: 0 }
const end2 = { hour: 23, minutes: 59, seconds: 59}
for (let i = 0; i < 7; i++) {
nonWorkingHours.push({
title: "Non working hours",
start: setSeconds(setMinutes(setHours(date, start1.hour), start1.minutes), start1.seconds),
end: setSeconds(setMinutes(setHours(date, end1.hour), end1.minutes), end1.seconds),
nonWorking: true,
split: i + 1,
draggable: false
})
nonWorkingHours.push({
title: "Non working hours",
start: setSeconds(setMinutes(setHours(date, start2.hour), start2.minutes), start2.seconds),
end: setSeconds(setMinutes(setHours(date, end2.hour), end2.minutes), end2.seconds),
nonWorking: true,
split: i + 1,
draggable: false
})
}
this.timelineData.tasks.push(...nonWorkingHours)
},
triggerToast(message) { triggerToast(message) {
this.errorToastMessage = message this.errorToastMessage = message
this.errorToast = true this.errorToast = true
}, },
async onEventMoved(event) { async onEventMoved(event) {
console.log(`ID from dragStop: ${event.event.id}`) console.log("drag", event)
// try { // try {
// await this.$axios.post_auth_data(`v1/overview/tasks/${event.event.id}`, {start: event.event.start, end: event.event.end}) // const newStartDate = addDays(event.event.start, differenceInCalendarDays(this.splitDays.filter((split) => {return split.id == event.newSplit})[0].date, event.event.start))
// const newEndDate = addDays(event.event.end, differenceInCalendarDays( newStartDate, event.event.start))
// await this.$axios.post_auth_data(`v1/overview/tasks/${event.event.id}`, {start: newStartDate, end: newEndDate})
// } catch(error) { // } catch(error) {
// this.triggerToast("Nepodarilo sa presunut ulohu!") // this.triggerToast("Nepodarilo sa presunut ulohu!")
// this.timelineData.tasks.filter(e => { // this.timelineData.tasks.filter(e => {
@@ -90,6 +144,18 @@ export default {
// } // }
// }) // })
// } // }
},
completeTask(id) {
this.timelineData.tasks.filter(task => {
if (task.id == id) {
task.completed = true
}
})
},
deleteTask(id) {
this.timelineData.tasks.splice(this.timelineData.tasks.findIndex(item => item.id === id), 1)
} }
} }
} }
+117 -2
View File
@@ -3,7 +3,7 @@
<A-team-filter @dayChanged="getDayData" /> <A-team-filter @dayChanged="getDayData" />
<W-loadable :content="timelineData" class="w-loadable" empty-message="Nepodarilo sa nacitat data!"> <W-loadable :content="timelineData" class="w-loadable" empty-message="Nepodarilo sa nacitat data!">
<Z-timeline /> <Z-timeline :calData="timelineData" :calConfig="config" :splits="splitDays" @eventMoved="onEventMoved" />
</W-loadable> </W-loadable>
<v-snackbar <v-snackbar
@@ -21,6 +21,8 @@
import ATeamFilter from './_components/a-team-filter.vue' import ATeamFilter from './_components/a-team-filter.vue'
import ZTimeline from '../tasks/_components/z-timeline.vue' import ZTimeline from '../tasks/_components/z-timeline.vue'
import { setHours, setMinutes, setSeconds } from 'date-fns'
export default { export default {
components: { components: {
ATeamFilter, ATeamFilter,
@@ -29,7 +31,119 @@ export default {
data() { data() {
return { return {
timelineData: {kokot: "kokot"} timelineData: null,
formatedTasks: {tasks: []},
errorToast: false,
errorToastMessage: "Nepodarilo sa nacitat data!",
dataLoaded: false,
splitDays: [],
config: {
date: new Date(),
scrollHour: 7,
}
}
},
methods: {
async getDayData(date) {
this.config.date = date
try {
this.timelineData = await this.$axios.get_auth_data('v1/overview/teams')
this.generateSplits()
this.formatData()
this.generateNonWorkingHours(date)
} catch(error) {
this.timelineData = {}
console.error(error)
this.triggerToast("Nepodarilo sa nacitat data!")
}
},
generateSplits() {
this.splitDays = []
for (let i = 0; i < this.timelineData.length; i++) {
this.timelineData[i].splitId = i + 1
this.splitDays.push({
id: i + 1,
firstName: this.timelineData[i].name,
name: this.timelineData[i].name + " " + this.timelineData[i].surname,
initials: this.timelineData[i].name.charAt(0).toUpperCase() + this.timelineData[i].surname.charAt(0).toUpperCase(),
unscheduledTime: Math.floor(this.timelineData[i].unscheduled_time / 60) + "," + String(this.timelineData[i].unscheduled_time % 60)[0]
})
}
},
formatData() {
this.timelineData.tasks = []
// format events
this.timelineData.forEach(user => {
user.tasks.forEach(event => {
event.name = user.name
event.surname = user.surname
event.start = new Date(event.starts_at)
event.end = new Date(event.ends_at)
event.split = user.splitId
})
this.timelineData.tasks.push(...user.tasks)
})
},
generateNonWorkingHours(date) {
let nonWorkingHours = []
let workStartHour = 23
this.timelineData.forEach(user => {
const start1 = { hour: 0, minutes: 0, seconds: 0}
const end1 = { hour: Number(user.work_hours_from.split(':')[0]), minutes: Number(user.work_hours_from.split(':')[1]) - 1, seconds: 0 }
const start2 = { hour: Number(user.work_hours_to.split(':')[0]), minutes: Number(user.work_hours_to.split(':')[1]) + 1, seconds: 0 }
const end2 = { hour: 23, minutes: 59, seconds: 59}
if (workStartHour > Number(user.work_hours_from.split(':')[0])) workStartHour = Number(user.work_hours_from.split(':')[0])
nonWorkingHours.push({
title: "Non working hours",
start: setSeconds(setMinutes(setHours(date, start1.hour), start1.minutes), start1.seconds),
end: setSeconds(setMinutes(setHours(date, end1.hour), end1.minutes), end1.seconds),
nonWorking: true,
split: user.splitId,
draggable: false
})
nonWorkingHours.push({
title: "Non working hours",
start: setSeconds(setMinutes(setHours(date, start2.hour), start2.minutes), start2.seconds),
end: setSeconds(setMinutes(setHours(date, end2.hour), end2.minutes), end2.seconds),
nonWorking: true,
split: user.splitId,
draggable: false
})
})
this.config.scrollHour = workStartHour - 1
this.timelineData.tasks.push(...nonWorkingHours)
},
triggerToast(message) {
this.errorToastMessage = message
this.errorToast = true
},
async onEventMoved(event) {
console.log("drag", event)
// try {
// const newStartDate = addDays(event.event.start, differenceInCalendarDays(this.splitDays.filter((split) => {return split.id == event.newSplit})[0].date, event.event.start))
// const newEndDate = addDays(event.event.end, differenceInCalendarDays( newStartDate, event.event.start))
// await this.$axios.post_auth_data(`v1/overview/tasks/${event.event.id}`, {start: newStartDate, end: newEndDate})
// } catch(error) {
// this.triggerToast("Nepodarilo sa presunut ulohu!")
// this.timelineData.tasks.filter(e => {
// if (e.id == event.event.id) {
// e.start = new Date(event.originalEvent.start)
// e.end = new Date(event.originalEvent.end)
// }
// })
// }
} }
} }
} }
@@ -44,4 +158,5 @@ export default {
@media (max-width: 1312px) @media (max-width: 1312px)
.w-loadable .w-loadable
height: calc( 100% - 202px ) height: calc( 100% - 202px )
</style> </style>
@@ -44,6 +44,7 @@ export default {
await this.$axios.post(`v1/tasks/${task.id}`, task) await this.$axios.post(`v1/tasks/${task.id}`, task)
this.show = false this.show = false
this.$emit('completeTask', task.id)
} catch (error) { } catch (error) {
this.$toast.error(error) this.$toast.error(error)
} }
@@ -39,6 +39,7 @@ export default {
try { try {
await this.$axios.delete(`v1/tasks/${this.task.id}`) await this.$axios.delete(`v1/tasks/${this.task.id}`)
this.show = false this.show = false
this.$emit('deleteTask', this.task.id)
} catch (error) { } catch (error) {
this.$toast.error(error) this.$toast.error(error)
} }