You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
375 lines
17 KiB
375 lines
17 KiB
<template> |
|
<div class="row"> |
|
<div class="col-md-12"> |
|
<div class="row"> |
|
<template v-for="(tracker, trackerIndex) in $store.state.trackers" v-bind:key="trackerIndex"> |
|
<div class="col-lg-4 col-md-6"> |
|
<div :class="'card ' + (tracker.isTimeBox ? 'bg-timebox' : 'bg-gradient-secondary')" |
|
:style="tracker.isTimeBox ? 'background: linear-gradient(90deg, grey ' + ((timeBoxTimeLeft(tracker) * 100) / tracker.timeBoxMinutes)+'% , black 100%':''"> |
|
<div class="card-body"> |
|
<div class="card-text"> |
|
<input type="text" |
|
v-model="tracker.number" |
|
class="form-control trackingNameField" |
|
@keydown="this.$store.commit('saveTrackers')"/> |
|
|
|
<div class="row"> |
|
<div v-if="tracker.tracking === true"> |
|
<div class="text-danger font-weight-bolder float-end tracker-time-info"> |
|
<div class="spinner-grow spinner-grow-sm" role="status"> |
|
<span class="sr-only">Tracking...</span> |
|
</div> |
|
Tracking |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<template v-if="!tracker.isTimeBox"> |
|
<div v-if="tracker.tracking === true"> |
|
<span class="float-end">{{ getTrackingStartTime(tracker) }}</span> |
|
<span v-if="tracker.tracking === true">Gestartet: </span> |
|
<br/> |
|
<span class="float-end">{{ currentTrackingRunningFor(tracker) }}</span> |
|
<span v-if="tracker.tracking === true">Läuft seit: </span> |
|
</div> |
|
|
|
<div> |
|
<span class="float-end">{{ getTotalTime(tracker) }}</span> |
|
<span class="current-tracker-info">Gesamt: </span> |
|
</div> |
|
|
|
<span class="float-end">{{ getTotalTimeToday(tracker) }}</span> |
|
<span class="">Heute: </span> |
|
|
|
<div class="row"> |
|
<div class="col-md-6" v-if="!tracker.tracking"> |
|
<button type="button" class="btn btn-primary tracker-action-button" |
|
@click="startTracking(tracker)"> |
|
<i class="far fa-play-circle"></i> <small>Starten</small> |
|
</button> |
|
</div> |
|
|
|
<div class="col-md-6" v-else> |
|
<button type="button" class="btn btn-danger tracker-action-button" |
|
@click="stopTracking(tracker)"> |
|
<i class="far fa-stop-circle"></i> <small>Stoppen</small> |
|
</button> |
|
</div> |
|
|
|
<div class="col-md-6"> |
|
<button class="btn btn-warning tracker-action-button" |
|
@click="archiveTracker(trackerIndex)" title="Archivieren"> |
|
<i class="fas fa-archive"></i> <small>Archivieren</small> |
|
</button> |
|
</div> |
|
|
|
<div class="col-md-6"> |
|
<button type="button" class="btn btn-info tracker-action-button" |
|
@click="openTasksForTracker(tracker)" title="Tasks"> |
|
<i class="fas fa-clipboard -check"></i> <small>Tasks |
|
{{ showOpenTasksForTracker(tracker.tasks) }}</small> |
|
</button> |
|
</div> |
|
|
|
<div class="col-md-6"> |
|
<button class="btn btn-danger tracker-action-button" |
|
@click="deleteTracker(trackerIndex)" title="Löschen"> |
|
<i class="fas fa-trash"></i> <small>Löschen</small> |
|
</button> |
|
</div> |
|
</div> |
|
</template> |
|
<template v-else> |
|
<div class="row"> |
|
<div v-if="tracker.tracking === true"> |
|
<span class="float-end">{{ getTrackingStartTime(tracker) }}</span> |
|
<span v-if="tracker.tracking === true">Gestartet: </span> |
|
<br/> |
|
<span class="float-end">{{ currentTrackingRunningFor(tracker) }}</span> |
|
<span v-if="tracker.tracking === true">Läuft seit: </span> |
|
</div> |
|
|
|
<template v-if="!tracker.timeBoxMinutes"> |
|
<div class="col-12"> |
|
Minuten: |
|
</div> |
|
<div class="col"> |
|
<button type="button" class="btn btn-secondary tracker-action-button" |
|
@click="startTimeBox(tracker, 15)"> |
|
<i class="far fa-play-circle"></i> 15 |
|
</button> |
|
</div> |
|
|
|
<div class="col"> |
|
<button type="button" class="btn btn-secondary tracker-action-button" |
|
@click="startTimeBox(tracker, 30)"> |
|
<i class="far fa-play-circle"></i> 30 |
|
</button> |
|
</div> |
|
|
|
<div class="col"> |
|
<button type="button" class="btn btn-secondary tracker-action-button" |
|
@click="startTimeBox(tracker, 60)"> |
|
<i class="far fa-play-circle"></i> 60 |
|
</button> |
|
</div> |
|
</template> |
|
|
|
<div class="col-12" |
|
v-if="tracker.timeBoxMinutes && timeBoxTimeLeft(tracker) > 0"> |
|
<span class="float-end">{{ timeBoxTimeLeft(tracker) }} Minuten</span> |
|
<span>Zeit übrig: </span> |
|
</div> |
|
<div class="col-12 text-center" v-if="timeBoxTimeLeft(tracker) <= 0"> |
|
<h5 class="text-danger text-bold">Abgeschlossen</h5> |
|
</div> |
|
|
|
<div class="col-md-12" v-if="tracker.tracking"> |
|
<button type="button" class="btn btn-danger tracker-action-button" |
|
@click="stopTracking(tracker)"> |
|
<i class="far fa-stop-circle"></i> <small>Stoppen</small> |
|
</button> |
|
</div> |
|
<div class="col-md-12" |
|
v-if="!tracker.tracking && tracker.timeBoxMinutes && timeBoxTimeLeft(tracker) > 0"> |
|
<button type="button" class="btn btn-primary tracker-action-button" |
|
@click="startTracking(tracker)"> |
|
<i class="far fa-play-circle"></i> <small>Starten</small> |
|
</button> |
|
</div> |
|
|
|
<div class="col-md-12"> |
|
<button class="btn btn-danger tracker-action-button" |
|
@click="deleteTracker(trackerIndex)" title="Löschen"> |
|
<i class="fas fa-trash"></i> <small>Löschen</small> |
|
</button> |
|
</div> |
|
</div> |
|
</template> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</template> |
|
</div> |
|
</div> |
|
</div> |
|
</template> |
|
|
|
<script> |
|
import moment from "moment"; |
|
import iziToast from "izitoast"; |
|
import {Offcanvas} from "bootstrap"; |
|
|
|
export default { |
|
name: "Trackers", |
|
data() { |
|
return { |
|
} |
|
}, |
|
computed: { |
|
trackers() { |
|
return this.$store.state.trackers; |
|
} |
|
}, |
|
methods: { |
|
startTracking(tracker, individual = false, timeBoxMinutes = null) { |
|
if (!individual) { |
|
this.stopActiveTracker(); |
|
} |
|
|
|
if (timeBoxMinutes) { |
|
tracker.timeBoxMinutes = timeBoxMinutes; |
|
} |
|
|
|
tracker.status = 'wip'; |
|
|
|
tracker.trackingStarted = moment(); |
|
tracker.tracking = true; |
|
|
|
this.$forceUpdate(); |
|
this.$store.commit('saveTrackers'); |
|
}, |
|
stopTracking(tracker) { |
|
tracker.trackingStopped = moment(); |
|
tracker.tracking = false; |
|
|
|
let minutesSpent = moment.duration( |
|
tracker.trackingStopped.diff(tracker.trackingStarted) |
|
).as('minutes'); |
|
|
|
if (minutesSpent > 0) { |
|
let historyEntry = { |
|
trackingStarted: tracker.trackingStarted, |
|
trackingStopped: tracker.trackingStopped, |
|
manually: false, |
|
minutes: Math.round(minutesSpent) |
|
}; |
|
|
|
tracker.history.pushToBeginning(historyEntry); |
|
} |
|
|
|
tracker.trackingStarted = null; |
|
tracker.trackingStopped = null; |
|
|
|
this.$store.commit('saveTrackers'); |
|
}, |
|
archiveTracker(index) { |
|
this.stopActiveTracker(); |
|
this.$store.commit('archiveTracker', index); |
|
this.$store.commit('saveTrackers'); |
|
}, |
|
currentTrackingRunningFor(tracker) { |
|
return this.timeWithPostFix(Math.round(moment.duration(moment().diff(tracker.trackingStarted)).as('minutes'))); |
|
}, |
|
getTotalTime(tracker, raw = false) { |
|
let totalTime = 0; |
|
|
|
if (tracker.history.length > 0) { |
|
tracker.history.forEach(function (historyEntry) { |
|
totalTime += Math.round(historyEntry.minutes); |
|
}); |
|
} |
|
|
|
if (tracker.tracking) { |
|
totalTime += Math.round(moment.duration(moment().diff(tracker.trackingStarted)).as('minutes')); |
|
} |
|
|
|
if (raw) { |
|
return totalTime; |
|
} else { |
|
return this.timeWithPostFix(totalTime); |
|
} |
|
}, |
|
getTotalTimeToday(tracker) { |
|
let totalTime = 0; |
|
|
|
if (tracker.history.length > 0) { |
|
tracker.history.forEach(function (historyEntry) { |
|
if (moment(historyEntry.trackingStarted).format("MMM Do YY") === moment().format("MMM Do YY")) { |
|
totalTime += Math.round(historyEntry.minutes); |
|
} |
|
}); |
|
} |
|
|
|
if (tracker.tracking) { |
|
totalTime += Math.round(moment.duration(moment().diff(tracker.trackingStarted)).as('minutes')); |
|
} |
|
|
|
return this.timeWithPostFix(totalTime); |
|
}, |
|
timeWithPostFix(time) { |
|
let postFix = ' Minute'; |
|
|
|
if (time >= 480 && this.$store.state.settings.showPT) { |
|
postFix = ' PT'; |
|
time = (time / 480).toFixed(1); |
|
} else if (time >= 60 || this.$store.state.settings.dontShowMinutes) { |
|
postFix = ' Stunde'; |
|
time = (time / 60).toFixed(2); |
|
} |
|
|
|
let plural = ''; |
|
|
|
if (((time > 1 || time <= 0) || this.$store.state.settings.dontShowMinutes) && postFix !== ' PT') { |
|
plural = 'n' |
|
} |
|
|
|
return time + postFix + plural; |
|
}, |
|
stopActiveTracker() { |
|
let vue = this; |
|
|
|
vue.trackers.forEach(function (tracker) { |
|
if (tracker.tracking === true) { |
|
vue.stopTracking(tracker); |
|
} |
|
}) |
|
}, |
|
getTrackingStartTime(tracker) { |
|
return moment(tracker.trackingStarted).format('LT'); |
|
}, |
|
isTrackerNumber(number) { |
|
return number.indexOf('#') >= 0; |
|
}, |
|
deleteTracker(index, archive = false) { |
|
let component = this; |
|
this.$store.commit("deleteTracker", index, archive); |
|
let message = 'Tracker "' + this.$store.state.trashed.number + '" wurde gelöscht'; |
|
|
|
iziToast.show({ |
|
message: message, |
|
color: 'blue', |
|
buttons: [ |
|
['<button><i class="fas fa-undo"></i></button>', function (instance, toast) { |
|
instance.hide({ |
|
transitionOut: 'fadeOutUp', |
|
onClosing: function(){ |
|
component.$store.commit('restoreTrashed'); |
|
component.$store.commit('saveTrackers'); |
|
} |
|
}, toast, 'buttonName'); |
|
}, true] |
|
] |
|
}); |
|
this.$store.commit('saveTrackers'); |
|
}, |
|
showOpenTasksForTracker(tasks) { |
|
let count = this.getOpenTasksForTracker(tasks); |
|
return count > 0 ? ' ('+count+' offen)' : ''; |
|
}, |
|
getOpenTasksForTracker(tasks) { |
|
if (!tasks) { |
|
return 0; |
|
} |
|
|
|
let counter = 0; |
|
|
|
tasks.forEach((task) => { |
|
if (!task.done) { |
|
counter++; |
|
} |
|
}); |
|
|
|
return counter; |
|
}, |
|
timeBoxTimeLeft(tracker) { |
|
return Number(tracker.timeBoxMinutes - this.getTotalTime(tracker, true)); |
|
}, |
|
startTimeBox(tracker, minutes) { |
|
Notification.requestPermission(); |
|
this.startTracking(tracker, false, minutes); |
|
}, |
|
openTasksForTracker(tracker) { |
|
this.$store.commit('selectTracker', tracker); |
|
|
|
setTimeout(() => { |
|
let canvas = document.getElementById('trackerTasksCanvas'); |
|
let tasksCanvas = new Offcanvas(canvas); |
|
tasksCanvas.show(); |
|
}, 50) |
|
}, |
|
} |
|
} |
|
</script> |
|
|
|
<style scoped> |
|
.tracker-time-info { |
|
margin-top: 1em; |
|
clear: both; |
|
} |
|
|
|
.trackingNameField { |
|
max-height: 1em; |
|
margin-bottom: 1px; |
|
} |
|
|
|
.bg-timebox input { |
|
color: white; |
|
} |
|
|
|
.bg-timebox input:focus { |
|
color: lightgray; |
|
} |
|
|
|
</style> |