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

<template>
<div class="row">
<div class="col-md-12">
<div class="row">
4 years ago
<template v-for="(tracker, trackerIndex) in 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";
4 years ago
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';
4 years ago
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) {
4 years ago
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(){
4 years ago
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);
4 years ago
},
openTasksForTracker(tracker) {
this.$store.commit('selectTracker', tracker);
setTimeout(() => {
4 years ago
let canvas = document.getElementById('trackerTasksCanvas');
let tasksCanvas = new Offcanvas(canvas);
tasksCanvas.show();
}, 50)
4 years ago
},
}
}
</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>