Time tracking you can host anywhere, built on Django. Full export support in multiple formats and easily extensible.
djangodockerhandcodedpythonself-hostedtime-trackingtimetracker
1import Vue from 'vue';
2
3import fetch from '../../fetch';
4
5
6export default {
7 namespaced: true,
8 state: {
9 all: [],
10 subtotal: 0,
11 total: 0,
12 },
13 getters: {
14 getSelectEntries: state => {
15 return state.all.map(entry => {
16 return {id: entry.url, text: entry.name};
17 });
18 },
19 getEntriesByDay: state => {
20 let allDays = [];
21 for (let i = 0; i < 5; i++) {
22 let day = Vue.prototype.$moment().subtract(i, 'day').format('YYYY-MM-DD');
23 allDays.push(day);
24 }
25
26 return allDays.map(date => {
27 let day = {
28 date: date,
29 duration: 0,
30 entries: state.all.filter(entry => {
31 return entry.date == date;
32 }),
33 };
34 day.entries.forEach(entry => {
35 day.duration += entry.duration;
36 });
37 return day;
38 });
39 },
40 getEntriesByDayTotal: (state, getters) => {
41 let entriesByDayTotal = 0;
42 getters.getEntriesByDay.forEach(day => {
43 entriesByDayTotal += day.duration;
44 });
45 return entriesByDayTotal;
46 },
47 },
48 mutations: {
49 setEntries: (state, entries) => state.all = entries,
50 setTotal: (state, total) => state.total = total,
51 setSubtotal: (state, subtotal) => state.subtotal = subtotal,
52 addEntry: (state, entry) => state.all.unshift(entry),
53 updateEntry: (state, opts) => Vue.set(state.all, opts.index, opts.entry),
54 removeEntry: (state, index) => Vue.delete(state.all, index),
55 },
56 actions: {
57 fetchEntries({commit}) {
58 commit('addLoading', 'entries', {root: true});
59 let getOptions = {
60 params: {
61 user: timestrapConfig.USER.ID,
62 min_date: Vue.prototype.$moment().subtract(5, 'day').format('YYYY-MM-DD'),
63 max_date: Vue.prototype.$moment().format('YYYY-MM-DD'),
64 },
65 };
66 fetch.get(timestrapConfig.API_URLS.ENTRIES, getOptions).then(response => {
67 commit('setEntries', response.data.results);
68 commit('setTotal', response.data.total_duration);
69 commit('setSubtotal', response.data.subtotal_duration);
70 commit('removeLoading', 'entries', {root: true});
71 }).catch(error => console.log(error));
72 },
73 createEntry({commit}, entry) {
74 fetch.post(timestrapConfig.API_URLS.ENTRIES, entry).then(response => {
75 commit('addEntry', response.data);
76 Vue.prototype.$bus.$emit('toast', {
77 title: 'New Entry Added',
78 message: 'Your new entry has been successfully added.',
79 });
80 }).catch(error => console.log(error));
81 },
82 editEntry({commit, state}, entry) {
83 fetch.put(entry.url, entry).then(response => {
84 const index = state.all.findIndex(item => {
85 return item.id === response.data.id;
86 });
87 commit('updateEntry', {index: index, entry: response.data});
88 }).catch(error => console.log(error));
89 },
90 deleteEntry({commit, state}, entry) {
91 const index = state.all.findIndex(item => {
92 return item.id === entry.id;
93 });
94 fetch.delete(state.all[index].url).then(() => {
95 commit('removeEntry', index);
96 }).catch(error => console.log(error));
97 },
98 },
99};