Browse Source

WIP timeboxes

modals-to-spa
stingl 4 years ago
parent
commit
82b1ea6295
  1. 20
      css/app.css
  2. 143
      index.html
  3. 60
      js/app.js

20
css/app.css

@ -182,3 +182,23 @@ nav, .card { @@ -182,3 +182,23 @@ nav, .card {
margin-top: 1em;
margin-left: 1em;
}
.dropdown-menu {
background: transparent;
border: none;
box-shadow: none;
}
.dropdown-menu-button {
width: 10em;
margin-bottom: 1em;
}
.bg-timebox {
background-color: lightcyan;
color: black;
}
.bg-timebox input {
color: black;
}

143
index.html

@ -40,7 +40,7 @@ @@ -40,7 +40,7 @@
<div class="row">
<template v-for="(ticket, ticketIndex) in tickets">
<div class="col-lg-4 col-md-6">
<div class="card bg-gradient-secondary">
<div :class="'card ' + (ticket.isTimeBox ? 'bg-timebox' : 'bg-gradient-secondary')">
<div class="card-body">
<div class="card-text">
<input type="text"
@ -48,6 +48,7 @@ @@ -48,6 +48,7 @@
class="form-control trackingNameField"
@keydown="updateStorage()"/>
<template v-if="!ticket.isTimeBox">
<div class="ticket-time-info">
<div v-if="ticket.tracking === true">
<div class="text-danger font-weight-bolder float-end">
@ -97,6 +98,82 @@ @@ -97,6 +98,82 @@
</button>
</div>
</div>
</template>
<template v-else>
<div class="row">
<div class="ticket-time-info">
<div v-if="ticket.tracking === true">
<div class="text-danger font-weight-bolder float-end">
<div class="spinner-grow spinner-grow-sm" role="status">
<span class="sr-only">Tracking...</span>
</div>
Tracking
</div>
</div>
</div>
<div v-if="ticket.tracking === true" class="ticket-time-info">
<span class="float-end">{{ getTrackingStartTime(ticket) }}</span>
<span v-if="ticket.tracking === true">Gestartet: </span>
<br/>
<span class="float-end">{{ currentTrackingRunningFor(ticket) }}</span>
<span v-if="ticket.tracking === true">Läuft seit: </span>
</div>
<template v-if="!ticket.timeBoxMinutes">
<div class="col-12">
Minuten:
</div>
<div class="col-4">
<button type="button" class="btn btn-secondary ticket-action-button"
@click="startTimeBox(ticket, 15)">
<i class="far fa-play-circle"></i> 15
</button>
</div>
<div class="col-4">
<button type="button" class="btn btn-secondary ticket-action-button"
@click="startTimeBox(ticket, 30)">
<i class="far fa-play-circle"></i> 30
</button>
</div>
<div class="col-4">
<button type="button" class="btn btn-secondary ticket-action-button"
@click="startTimeBox(ticket, 60)">
<i class="far fa-play-circle"></i> 60
</button>
</div>
</template>
<div class="col-12" v-if="ticket.timeBoxMinutes && timeBoxTimeLeft(ticket) > 0">
<span class="float-end">{{ timeBoxTimeLeft(ticket) }} Minuten</span>
<span>Zeit übrig: </span>
</div>
<div class="col-12 text-center" v-if="timeBoxTimeLeft(ticket) < 1">
<h5 class="text-danger text-bold">Abgeschlossen</h5>
</div>
<div class="col-md-12" v-if="ticket.tracking">
<button type="button" class="btn btn-danger ticket-action-button"
@click="stopTracking(ticket)">
<i class="far fa-stop-circle"></i>
</button>
</div>
<div class="col-md-12" v-if="!ticket.tracking && ticket.timeBoxMinutes && timeBoxTimeLeft(ticket) > 0">
<button type="button" class="btn btn-info ticket-action-button"
@click="startTracking(ticket)">
<i class="far fa-play-circle"></i>
</button>
</div>
<div class="col-md-12">
<button class="btn btn-secondary ticket-action-button" data-bs-dismiss="modal"
@click="archiveTicket(ticketIndex)" title="Archivieren">
<i class="fas fa-archive"></i>
</button>
</div>
</div>
</template>
</div>
</div>
</div>
@ -252,7 +329,7 @@ @@ -252,7 +329,7 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-clock"></i> Tracker</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
@ -345,7 +422,7 @@ @@ -345,7 +422,7 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-archive"></i> Archivierte Tracker</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
@ -413,7 +490,7 @@ @@ -413,7 +490,7 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-sliders-h"></i> Einstellungen</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body row">
<div class="col-md-6">
@ -500,8 +577,8 @@ @@ -500,8 +577,8 @@
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" v-on:click="updateStorage()" data-dismiss="modal">Speichern</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Schließen</button>
<button class="btn btn-primary" v-on:click="updateStorage()" data-bs-dismiss="modal">Speichern</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
</div>
</div>
</div>
@ -514,7 +591,7 @@ @@ -514,7 +591,7 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-history"></i> History von {{ selectedTicket.number }}</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<ul class="list-group ticket-history">
@ -578,7 +655,7 @@ @@ -578,7 +655,7 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-user-edit"></i> Buchung für {{selectedTicket.number}}</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="form-group">
@ -587,10 +664,10 @@ @@ -587,10 +664,10 @@
</div>
<div class="row">
<div class="col-6">
<a href="javascript:" class="btn btn-success btn-full-width" data-dismiss="modal" @click="makeCustomBooking()">+ Hinzubuchen</a>
<a href="javascript:" class="btn btn-success btn-full-width" data-bs-dismiss="modal" @click="makeCustomBooking()">+ Hinzubuchen</a>
</div>
<div class="col-6">
<a href="javascript:" class="btn btn-danger btn-full-width" data-dismiss="modal" @click="makeCustomBooking(true)">- Abbuchen</a>
<a href="javascript:" class="btn btn-danger btn-full-width" data-bs-dismiss="modal" @click="makeCustomBooking(true)">- Abbuchen</a>
</div>
</div>
</div>
@ -605,7 +682,7 @@ @@ -605,7 +682,7 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-random"></i> Portal Switcher</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
@ -647,7 +724,7 @@ @@ -647,7 +724,7 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-code"></i> Snippet Space</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
@ -677,21 +754,21 @@ @@ -677,21 +754,21 @@
<div class="container">
<div class="row">
<div class="col">
<a type="button"
class="btn btn-success text-light bottom-menu-item"
@click="addTrackedTicket()"
data-bs-toggle="tooltip"
data-bs-placement="left"
title="Neuer Tracker">
<div class="dropup">
<button class="btn btn-success text-light bottom-menu-item" type="button" id="create-dropdown-menu" data-bs-toggle="dropdown" aria-expanded="false" title="Neu">
<i class="fas fa-plus"></i>
</a>
</button>
<ul class="dropdown-menu" aria-labelledby="create-dropdown-menu">
<li><button class="btn btn-light dropdown-menu-button" type="button" @click="addTrackedTicket()">Tracker</button></li>
<li><button class="btn btn-light dropdown-menu-button" type="button" @click="addTimeBox()">Timebox</button></li>
</ul>
</div>
</div>
<div class="col" v-if="tickets.length > 0">
<a type="button"
class="btn btn-primary text-light bottom-menu-item"
data-toggle="modal"
data-target="#showTicketsModal"
data-bs-toggle="tooltip"
data-bs-toggle="modal"
data-bs-target="#showTicketsModal"
data-bs-placement="top"
title="Alle Tracker">
<i class="fas fa-clock"></i>
@ -700,9 +777,8 @@ @@ -700,9 +777,8 @@
<div class="col" v-if="archive.length > 0">
<a type="button"
class="btn btn-secondary text-dark bottom-menu-item"
data-toggle="modal"
data-target="#showArchivedTicketsModal"
data-bs-toggle="tooltip"
data-bs-toggle="modal"
data-bs-target="#showArchivedTicketsModal"
data-bs-placement="top"
title="Archivierte Tracker">
<i class="fas fa-archive"></i>
@ -711,9 +787,8 @@ @@ -711,9 +787,8 @@
<div class="col" v-if="experimental.portalSwitcher">
<a type="button"
class="btn btn-warning text-light bottom-menu-item"
data-toggle="modal"
data-target="#switcherModal"
data-bs-toggle="tooltip"
data-bs-toggle="modal"
data-bs-target="#switcherModal"
data-bs-placement="top"
title="Portal Switcher">
<i class="fas fa-random"></i>
@ -722,9 +797,8 @@ @@ -722,9 +797,8 @@
<div class="col" v-if="experimental.snippetSpace">
<a type="button"
class="btn btn-info text-light bottom-menu-item"
data-toggle="modal"
data-target="#snippetSpaceModal"
data-bs-toggle="tooltip"
data-bs-toggle="modal"
data-bs-target="#snippetSpaceModal"
data-bs-placement="top"
title="Snippet Space">
<i class="fas fa-code"></i>
@ -733,9 +807,8 @@ @@ -733,9 +807,8 @@
<div class="col">
<a type="button"
class="btn btn-dark text-light bottom-menu-item"
data-toggle="modal"
data-target="#settingsModal"
data-bs-toggle="tooltip"
data-bs-toggle="modal"
data-bs-target="#settingsModal"
data-bs-placement="left"
title="Einstellungen">
<i class="fas fa-sliders-h"></i>
@ -754,7 +827,7 @@ @@ -754,7 +827,7 @@
<script src="https://unpkg.com/vue@next"></script>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script src="js/app.js"></script>
</body>

60
js/app.js

@ -80,6 +80,11 @@ const TimeTrack = { @@ -80,6 +80,11 @@ const TimeTrack = {
vue.updateStorage();
}, 5000);
}
Notification.requestPermission();
setInterval(() => {
vue.checkTimeBoxes();
}, 10000);
},
methods: {
loadTooltips() {
@ -140,22 +145,40 @@ const TimeTrack = { @@ -140,22 +145,40 @@ const TimeTrack = {
this.updateStorage();
},
addTrackedTicket() {
let newTicket = {
this.tickets.push({
tracking: false,
number: '#',
trackingStarted: null,
trackingStopped: null,
history: []
};
});
this.tickets.push(newTicket);
this.updateStorage();
},
startTracking(ticket, individual = false) {
addTimeBox() {
this.tickets.push({
tracking: false,
number: 'Timebox ',
trackingStarted: null,
trackingStopped: null,
isTimeBox: true,
history: []
});
this.updateStorage();
},
startTimeBox(ticket, minutes) {
this.startTracking(ticket, false, minutes);
},
startTracking(ticket, individual = false, timeBoxMinutes = null) {
if (!individual) {
this.stopTrackingTicket();
}
if (timeBoxMinutes) {
ticket.timeBoxMinutes = timeBoxMinutes;
}
ticket.status = 'wip';
ticket.trackingStarted = moment();
@ -233,7 +256,7 @@ const TimeTrack = { @@ -233,7 +256,7 @@ const TimeTrack = {
currentTrackingRunningFor(ticket) {
return this.timeWithPostFix(Math.round(moment.duration(moment().diff(ticket.trackingStarted)).as('minutes')));
},
getTotalTime(ticket) {
getTotalTime(ticket, raw = false) {
let totalTime = 0;
if (ticket.history.length > 0) {
@ -246,7 +269,11 @@ const TimeTrack = { @@ -246,7 +269,11 @@ const TimeTrack = {
totalTime += Math.round(moment.duration(moment().diff(ticket.trackingStarted)).as('minutes'));
}
if (raw) {
return totalTime;
} else {
return this.timeWithPostFix(totalTime);
}
},
getTotalTimeToday(ticket) {
let totalTime = 0;
@ -540,6 +567,29 @@ const TimeTrack = { @@ -540,6 +567,29 @@ const TimeTrack = {
message: 'Code kopiert!',
color: 'green'
});
},
checkTimeBoxes() {
let vue = this;
this.tickets.forEach((ticket) => {
if (ticket.isTimeBox && vue.timeBoxTimeLeft(ticket) <= 0) {
vue.stopTracking(ticket);
if (Notification.permission) {
new Notification('Timebox zu Ende', {
body: 'Zeit für "'+ticket.number+'" ist abgelaufen!',
icon: '/timetrack/assets/img/favicon.ico'
});
} else {
iziToast.show({
message: 'Zeit für "'+ticket.number+'" ist abgelaufen!',
color: 'red'
});
}
}
});
},
timeBoxTimeLeft(ticket) {
return Number(ticket.timeBoxMinutes - this.getTotalTime(ticket, true));
}
},
watch: {