Time tracking you can host anywhere, built on Django. Full export support in multiple formats and easily extensible.
djangodockerhandcodedpythonself-hostedtime-trackingtimetracker
1<template>
2<div
3 v-if="$perms.view_entry"
4 class="container-fluid">
5 <entry-modal
6 v-if="modalShow"
7 :entry="modalEntry"
8 @close="modalShow = false"/>
9
10 <chart :entries="entries"/>
11
12 <div id="entry-rows">
13 <div
14 v-for="(entryBlock, blockIndex) in entries"
15 :index="blockIndex"
16 :key="entryBlock.id">
17 <div class="row inset-row">
18 <div class="col-6">
19 <h2 class="display-4">
20 {{ $moment(entryBlock.date).format('ddd[,] MMMM Do') }}
21 </h2>
22 </div>
23 <div class="col-6 d-flex align-items-center justify-content-end h4 mb-0">
24 <span class="badge badge-success mb-0">
25 {{ $moment.duration(entryBlock.duration, 'hours').format('h[h] m[m]') }}
26 </span>
27 </div>
28 </div>
29 <div>
30 <template v-if="entryBlock.entries.length > 0">
31 <entry
32 v-for="(entry, entryIndex) in entryBlock.entries"
33 :entry="entry"
34 :index="entryIndex"
35 :key="entry.id"
36 :editable="editable"
37 @modal="modalToggle(entry)"
38 @delete-entry="deleteEntry(entry)"/>
39 </template>
40 <template v-else>
41 <div class="entry row py-3 bg-light small">
42 <div class="col">
43 <strong>No entries</strong> for this day.
44 </div>
45 </div>
46 </template>
47 </div>
48 </div>
49
50 <div class="row bg-success text-white py-2 border-0">
51 <div class="ml-auto col-sm-2 text-right">
52 Subtotal<br>
53 <strong>Total</strong>
54 </div>
55 <div class="col-sm-4">
56 <icon
57 :icon="['fas', 'clock']"
58 class="mr-1"/>
59 {{ $moment.duration(subtotal, 'hours').format('d[d] h[h] m[m]') }}<br>
60 <icon
61 :icon="['fas', 'clock']"
62 class="mr-1"/>
63 <strong>{{ $moment.duration(total, 'hours').format('d[d] h[h] m[m]') }}</strong>
64 </div>
65 </div>
66 </div>
67</div>
68</template>
69
70
71<script>
72import {mapGetters, mapActions, mapState} from 'vuex';
73
74import Datepicker from '../datepicker.vue';
75import Entry from '../entry.vue';
76import EntryModal from './entry-modal.vue';
77import Chart from './chart.vue';
78
79
80export default {
81 components: {
82 Datepicker,
83 Entry,
84 EntryModal,
85 Chart,
86 },
87 data() {
88 return {
89 editable: true,
90 modalEntry: null,
91 modalShow: false,
92 };
93 },
94 computed: {
95 ...mapState({
96 total: state => state.entries.total,
97 }),
98 ...mapGetters({
99 entries: 'entries/getEntriesByDay',
100 subtotal: 'entries/getEntriesByDayTotal',
101 }),
102 },
103 methods: {
104 ...mapActions('entries', [
105 'fetchEntries',
106 ]),
107 modalToggle: function(entry) {
108 if (typeof(entry) !== 'undefined') this.modalEntry = entry;
109 else this.modalEntry = null;
110 this.modalShow = !this.modalShow;
111 },
112 },
113};
114</script>