mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 22:39:34 +00:00
[Refactoring] Architecture
This commit is contained in:
1 parent
9af01fc34b
commit
b374a85eac
34 files changed
+103
-51
No files matched your search
@@ -0,0 +1,3 @@
|
||||
<svg width="9" height="16" viewBox="0 0 9 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.70711 0.292893C1.31658 -0.0976311 0.683418 -0.0976311 0.292893 0.292893C-0.0976311 0.683418 -0.0976311 1.31658 0.292893 1.70711L1.70711 0.292893ZM8 8L8.70711 8.70711C9.09763 8.31658 9.09763 7.68342 8.70711 7.29289L8 8ZM0.292893 14.2929C-0.0976311 14.6834 -0.0976311 15.3166 0.292893 15.7071C0.683418 16.0976 1.31658 16.0976 1.70711 15.7071L0.292893 14.2929ZM0.292893 1.70711L7.29289 8.70711L8.70711 7.29289L1.70711 0.292893L0.292893 1.70711ZM7.29289 7.29289L0.292893 14.2929L1.70711 15.7071L8.70711 8.70711L7.29289 7.29289Z" fill="#14181F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 655 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="10" height="17" viewBox="0 0 10 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.79289 16.2071C8.18342 16.5976 8.81658 16.5976 9.20711 16.2071C9.59763 15.8166 9.59763 15.1834 9.20711 14.7929L7.79289 16.2071ZM1.5 8.5L0.792893 7.79289C0.402369 8.18342 0.402369 8.81658 0.792893 9.20711L1.5 8.5ZM9.20711 2.20711C9.59763 1.81658 9.59763 1.18342 9.20711 0.792893C8.81658 0.402369 8.18342 0.402369 7.79289 0.792893L9.20711 2.20711ZM9.20711 14.7929L2.20711 7.79289L0.792893 9.20711L7.79289 16.2071L9.20711 14.7929ZM2.20711 9.20711L9.20711 2.20711L7.79289 0.792893L0.792893 7.79289L2.20711 9.20711Z" fill="#14181F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 643 B |
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<v-row class="px-10 py-8 subheader">
|
||||
<v-select
|
||||
v-model="selectedEmployee"
|
||||
class="subheader-input"
|
||||
:items="employees"
|
||||
label="Zamestnanec"
|
||||
variant="outlined"
|
||||
></v-select>
|
||||
<div class="d-flex date-wrapper">
|
||||
<v-btn class="subheader-icon-button " variant="outlined"><img :src="require('./_img/icon-prev.svg')" alt=""></v-btn>
|
||||
<v-text-field
|
||||
class="subheader-input disabled"
|
||||
:items="items"
|
||||
label="Dátum"
|
||||
v-model="selectedDate"
|
||||
variant="outlined"
|
||||
append-inner-icon="mdi-calendar-blank-outline"
|
||||
></v-text-field>
|
||||
<v-btn class="subheader-icon-button " variant="outlined"><img :src="require('./_img/icon-next.svg')" alt=""></v-btn>
|
||||
</div>
|
||||
<v-text-field
|
||||
class="subheader-input"
|
||||
:items="items"
|
||||
label="Nenaplánované hodiny"
|
||||
v-model="plannedTime"
|
||||
variant="outlined"
|
||||
></v-text-field>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
employees: ['Foo', 'Bar', 'Fizz', 'Buzz'],
|
||||
selectedDate: "13.11.2022 - 17.11.2022",
|
||||
plannedTime: "35 h",
|
||||
selectedEmployee: 'Bar'
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.subheader
|
||||
max-width: 100%
|
||||
margin: 0
|
||||
border-bottom: 1px solid rgba(33, 33, 33, 0.08)
|
||||
border-top: 1px solid rgba(33, 33, 33, 0.08)
|
||||
gap: 30px
|
||||
flex-wrap: wrap
|
||||
|
||||
.subheader-icon-button
|
||||
border: 1px solid #ABABAB
|
||||
height: 56px
|
||||
width: 56px
|
||||
min-width: auto !important
|
||||
padding: 0
|
||||
margin: 0 10px
|
||||
|
||||
&:deep(.v-btn__append)
|
||||
margin: 0
|
||||
|
||||
.subheader-input
|
||||
max-width: 250px !important
|
||||
min-width: 180px
|
||||
|
||||
&:deep(.v-input__details)
|
||||
display: none
|
||||
|
||||
&.disabled
|
||||
pointer-events: none !important
|
||||
|
||||
|
||||
.date-wrapper
|
||||
flex: 0 1 402px
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,326 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-sheet tile height="54" color="grey lighten-3" class="d-flex">
|
||||
<v-btn icon class="ma-2" @click="$refs.calendar.prev()">
|
||||
<v-icon>mdi-chevron-left</v-icon>
|
||||
</v-btn>
|
||||
<v-select v-model="type" :items="types" dense outlined hide-details class="ma-2" label="type"></v-select>
|
||||
<v-select v-model="mode" :items="modes" dense outlined hide-details label="event-overlap-mode" class="ma-2"></v-select>
|
||||
<v-select v-model="weekday" :items="weekdays" dense outlined hide-details label="weekdays" class="ma-2"></v-select>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon class="ma-2" @click="$refs.calendar.next()">
|
||||
<v-icon>mdi-chevron-right</v-icon>
|
||||
</v-btn>
|
||||
</v-sheet>
|
||||
<v-sheet height="600">
|
||||
<v-calendar ref="calendar" v-model="value" :weekdays="weekday" :type="type" :events="events" :event-overlap-mode="mode" :event-overlap-threshold="60" :event-color="getEventColor" :event-ripple="false" @change="getEvents" @mousedown:event="startDrag" @mousedown:time="startTime" @mousemove:time="mouseMove" @mouseup:time="endDrag" @mouseleave="cancelDrag">
|
||||
<template #event="{ event, timed }">
|
||||
<div class="pl-1" v-html="getEventHTML(event, timed)"></div>
|
||||
<div v-if="timed" class="v-event-drag-bottom" @mousedown.stop="extendBottom(event)"></div>
|
||||
</template>
|
||||
<template #day-body="{ date, week }">
|
||||
<div class="v-current-time" :class="{ first: date === week[0].date }" :style="{ top: nowY }">
|
||||
<div class="v-current-time-time">
|
||||
{{ nowTime }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</v-calendar>
|
||||
<!-- {{ lastEvent }} -->
|
||||
</v-sheet>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
type: 'day',
|
||||
types: ['month', 'week', 'day', '4day'],
|
||||
mode: 'stack',
|
||||
modes: ['stack', 'column'],
|
||||
weekday: [0, 1, 2, 3, 4, 5, 6],
|
||||
weekdays: [
|
||||
{ text: 'Sun - Sat', value: [0, 1, 2, 3, 4, 5, 6] },
|
||||
{ text: 'Mon - Sun', value: [1, 2, 3, 4, 5, 6, 0] },
|
||||
{ text: 'Mon - Fri', value: [1, 2, 3, 4, 5] },
|
||||
{ text: 'Mon, Wed, Fri', value: [1, 3, 5] }
|
||||
],
|
||||
value: '',
|
||||
events: [],
|
||||
colors: ['#2196F3', '#3F51B5', '#673AB7', '#00BCD4', '#4CAF50', '#FF9800', '#757575'],
|
||||
names: ['Meeting', 'Holiday', 'PTO', 'Travel', 'Event', 'Birthday', 'Conference', 'Party'],
|
||||
|
||||
dragEvent: null,
|
||||
dragStart: null,
|
||||
lastEvent: '',
|
||||
createEvent: null,
|
||||
createStart: null,
|
||||
extendOriginal: null,
|
||||
|
||||
isMounted: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
nowY() {
|
||||
const cal = this.$refs.calendar
|
||||
if (!cal && !this.isMounted) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return cal.timeToY(cal.times.now) + 'px'
|
||||
},
|
||||
|
||||
nowTime() {
|
||||
const cal = this.$refs.calendar
|
||||
if (!cal && !this.isMounted) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return cal.formatTime(cal.times.now)
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const cal = this.$refs.calendar
|
||||
this.isMounted = true
|
||||
|
||||
// scroll to the current time
|
||||
const minutes = cal.times.now.hour * 60 + cal.times.now.minute
|
||||
const firstTime = Math.max(0, minutes - (minutes % 30) - 30)
|
||||
cal.scrollToTime(firstTime)
|
||||
|
||||
// every minute update the current time bar
|
||||
setInterval(() => cal.updateTimes(), 60 * 1000)
|
||||
},
|
||||
|
||||
methods: {
|
||||
getEvents({ start, end }) {
|
||||
const events = []
|
||||
|
||||
const min = new Date(`${start.date}T00:00:00`)
|
||||
const max = new Date(`${end.date}T23:59:59`)
|
||||
const days = (max.getTime() - min.getTime()) / 86400000
|
||||
const eventCount = this.rnd(days, days + 20)
|
||||
|
||||
for (let i = 0; i < eventCount; i++) {
|
||||
const allDay = this.rnd(0, 3) === 0
|
||||
const firstTimestamp = this.rnd(min.getTime(), max.getTime())
|
||||
const first = new Date(firstTimestamp - (firstTimestamp % 900000))
|
||||
const secondTimestamp = this.rnd(2, allDay ? 288 : 8) * 900000
|
||||
const second = new Date(first.getTime() + secondTimestamp)
|
||||
|
||||
events.push({
|
||||
name: this.names[this.rnd(0, this.names.length - 1)],
|
||||
start: this.formatDate(first, !allDay),
|
||||
end: this.formatDate(second, !allDay),
|
||||
color: this.colors[this.rnd(0, this.colors.length - 1)]
|
||||
})
|
||||
}
|
||||
|
||||
this.events = events
|
||||
},
|
||||
getEventColor(event) {
|
||||
const rgb = parseInt(event.color.substring(1), 16)
|
||||
const r = (rgb >> 16) & 0xff
|
||||
const g = (rgb >> 8) & 0xff
|
||||
const b = (rgb >> 0) & 0xff
|
||||
|
||||
return event === this.dragEvent ? `rgba(${r}, ${g}, ${b}, 0.5)` : event === this.createEvent ? `rgba(${r}, ${g}, ${b}, 0.5)` : event.color
|
||||
},
|
||||
getEventHTML(event, timed) {
|
||||
const cal = this.$refs.calendar
|
||||
let name = event.name
|
||||
if (event.start.hasTime) {
|
||||
if (timed) {
|
||||
const showStart = event.start.hour < 12 && event.end.hour >= 12
|
||||
const start = cal.formatTime(event.start, showStart)
|
||||
const end = cal.formatTime(event.end, true)
|
||||
// const singline = diffMinutes(event.start, event.end) <= this.parsedEventOverlapThreshold
|
||||
const singline = event.end - event.start <= this.parsedEventOverlapThreshold
|
||||
const separator = singline ? ', ' : '<br>'
|
||||
return `<strong>${name}</strong>${separator}${start} - ${end}`
|
||||
} else {
|
||||
const time = this.formatTime(event.start, true)
|
||||
return `<strong>${time}</strong> ${name}`
|
||||
}
|
||||
}
|
||||
return name
|
||||
},
|
||||
rnd(a, b) {
|
||||
return Math.floor((b - a + 1) * Math.random()) + a
|
||||
},
|
||||
formatDate(a, withTime) {
|
||||
return withTime ? `${a.getFullYear()}-${a.getMonth() + 1}-${a.getDate()} ${a.getHours()}:${a.getMinutes()}` : `${a.getFullYear()}-${a.getMonth() + 1}-${a.getDate()}`
|
||||
},
|
||||
eventMove(e) {
|
||||
console.log('eventMove', e)
|
||||
},
|
||||
startDrag(e) {
|
||||
if (e.event && e.timed) {
|
||||
this.dragEvent = e.event
|
||||
this.dragTime = null
|
||||
this.extendOriginal = null
|
||||
}
|
||||
|
||||
this.lastEvent = 'startDrag'
|
||||
},
|
||||
startTime(e) {
|
||||
const mouse = this.toDate(e)
|
||||
|
||||
if (this.dragEvent && this.dragTime === null) {
|
||||
const start = this.toDate(this.dragEvent.start)
|
||||
|
||||
this.dragTime = mouse.getTime() - start.getTime()
|
||||
} else {
|
||||
this.createStart = this.roundTime(mouse.getTime())
|
||||
this.createEvent = {
|
||||
name: '(no title)',
|
||||
start: this.toTimestamp(new Date(this.createStart)),
|
||||
end: this.toTimestamp(new Date(this.createStart)),
|
||||
color: this.colors[this.rnd(0, this.colors.length - 1)]
|
||||
}
|
||||
this.events.push(this.createEvent)
|
||||
}
|
||||
this.lastEvent = 'startTime'
|
||||
},
|
||||
extendBottom(event) {
|
||||
this.createEvent = event
|
||||
this.createStart = this.toDate(event.start).getTime()
|
||||
this.extendOriginal = event.end
|
||||
},
|
||||
mouseMoveEvent(e) {
|
||||
console.log('mouseMoveEvent', e)
|
||||
},
|
||||
mouseMove(e) {
|
||||
if (this.dragEvent && this.dragTime !== null) {
|
||||
const start = this.toDate(this.dragEvent.start)
|
||||
const end = this.toDate(this.dragEvent.end)
|
||||
const duration = end.getTime() - start.getTime()
|
||||
const mouse = this.toDate(e)
|
||||
|
||||
const newStartTime = mouse.getTime() - this.dragTime
|
||||
const newStart = new Date(this.roundTime(newStartTime))
|
||||
const newEnd = new Date(newStart.getTime() + duration)
|
||||
|
||||
this.dragEvent.start = this.toTimestamp(newStart)
|
||||
this.dragEvent.end = this.toTimestamp(newEnd)
|
||||
} else if (this.createEvent && this.createStart !== null) {
|
||||
const mouse = this.toDate(e).getTime()
|
||||
const mouseRounded = this.roundTime(mouse, false)
|
||||
const min = Math.min(mouseRounded, this.createStart)
|
||||
const max = Math.max(mouseRounded, this.createStart)
|
||||
|
||||
this.createEvent.start = this.toTimestamp(new Date(min))
|
||||
this.createEvent.end = this.toTimestamp(new Date(max))
|
||||
}
|
||||
},
|
||||
endDrag(e) {
|
||||
this.dragTime = null
|
||||
this.dragEvent = null
|
||||
this.createEvent = null
|
||||
this.createStart = null
|
||||
this.extendOriginal = null
|
||||
|
||||
this.lastEvent = 'endDrag'
|
||||
},
|
||||
cancelDrag(e) {
|
||||
if (this.createEvent) {
|
||||
if (this.extendOriginal) {
|
||||
this.createEvent.end = this.extendOriginal
|
||||
} else {
|
||||
const i = this.events.indexOf(this.createEvent)
|
||||
if (i !== -1) {
|
||||
this.events.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.createEvent = null
|
||||
this.createStart = null
|
||||
this.dragTime = null
|
||||
this.dragEvent = null
|
||||
|
||||
this.lastEvent = 'cancelDrag'
|
||||
},
|
||||
roundTime(time, down = true) {
|
||||
const roundDownTime = 15 * 60 * 1000 // 15 minutes
|
||||
|
||||
return down ? time - (time % roundDownTime) : time + (roundDownTime - (time % roundDownTime))
|
||||
},
|
||||
toDate(tms) {
|
||||
return typeof tms === 'string' ? new Date(tms) : new Date(tms.year, tms.month - 1, tms.day, tms.hour, tms.minute)
|
||||
},
|
||||
toTimestamp(date) {
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.v-calendar
|
||||
user-select: none
|
||||
-webkit-user-select: none
|
||||
|
||||
.v-event-drag-bottom
|
||||
position: absolute
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 4px
|
||||
height: 4px
|
||||
cursor: ns-resize
|
||||
|
||||
.v-event-drag-bottom::after
|
||||
display: none
|
||||
position: absolute
|
||||
left: 50%
|
||||
height: 4px
|
||||
border-top: 1px solid white
|
||||
border-bottom: 1px solid white
|
||||
width: 20px
|
||||
margin-left: -10px
|
||||
opacity: 0.8
|
||||
content: ''
|
||||
|
||||
.v-event-timed:hover .v-event-drag-bottom::after
|
||||
display: block
|
||||
|
||||
.v-current-time
|
||||
height: 1px
|
||||
background-color: red
|
||||
position: absolute
|
||||
left: -1px
|
||||
right: 0
|
||||
pointer-events: none
|
||||
|
||||
&.first::after
|
||||
content: ''
|
||||
position: absolute
|
||||
background-color: red
|
||||
width: 7px
|
||||
height: 7px
|
||||
border-radius: 3.5px
|
||||
margin-top: -3px
|
||||
margin-left: -3px
|
||||
|
||||
|
||||
.v-current-time-time
|
||||
display: none
|
||||
|
||||
|
||||
&.first .v-current-time-time
|
||||
display: block
|
||||
position: absolute
|
||||
left: -60px
|
||||
color: red
|
||||
font-size: 10px
|
||||
padding: 1px
|
||||
background-color: white
|
||||
width: 50px
|
||||
height: 20px
|
||||
text-align: right
|
||||
top: -9px
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<A-task-filter />
|
||||
<Z-timeline />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ATaskFilter from './_components/a-tasks-filter.vue'
|
||||
import ZTimeline from './_components/z-timeline.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ATaskFilter,
|
||||
ZTimeline,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
Reference in new issue
Block a user