Added an option to reset timers daily
3
src/assets/cross.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x" viewBox="0 0 16 16">
|
||||||
|
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 332 B |
4
src/assets/gear.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#f7f7f7" class="bi bi-gear" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z"/>
|
||||||
|
<path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115l.094-.319z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1 +1,3 @@
|
|||||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="512px" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><path d="M224,435.8V76.1c0-6.7-5.4-12.1-12.2-12.1h-71.6c-6.8,0-12.2,5.4-12.2,12.1v359.7c0,6.7,5.4,12.2,12.2,12.2h71.6 C218.6,448,224,442.6,224,435.8z"/><path d="M371.8,64h-71.6c-6.7,0-12.2,5.4-12.2,12.1v359.7c0,6.7,5.4,12.2,12.2,12.2h71.6c6.7,0,12.2-5.4,12.2-12.2V76.1 C384,69.4,378.6,64,371.8,64z"/></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pause-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M5.5 3.5A1.5 1.5 0 0 1 7 5v6a1.5 1.5 0 0 1-3 0V5a1.5 1.5 0 0 1 1.5-1.5zm5 0A1.5 1.5 0 0 1 12 5v6a1.5 1.5 0 0 1-3 0V5a1.5 1.5 0 0 1 1.5-1.5z"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 664 B After Width: | Height: | Size: 289 B |
@@ -1 +1,3 @@
|
|||||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="512px" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M405.2,232.9L126.8,67.2c-3.4-2-6.9-3.2-10.9-3.2c-10.9,0-19.8,9-19.8,20H96v344h0.1c0,11,8.9,20,19.8,20 c4.1,0,7.5-1.4,11.2-3.4l278.1-165.5c6.6-5.5,10.8-13.8,10.8-23.1C416,246.7,411.8,238.5,405.2,232.9z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-play-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="m11.596 8.697-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393z"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 273 B |
@@ -1 +1,4 @@
|
|||||||
<?xml version="1.0" ?><svg fill="none" height="24" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><polyline stroke="#f7f7f7" points="3 6 5 6 21 6"/><path stroke="#f7f7f7" d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line stroke="#f7f7f7" x1="10" x2="10" y1="11" y2="17"/><line stroke="#f7f7f7" x1="14" x2="14" y1="11" y2="17"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#f7f7f7" class="bi bi-trash" viewBox="0 0 16 16">
|
||||||
|
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
|
||||||
|
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 470 B After Width: | Height: | Size: 568 B |
@@ -6,7 +6,7 @@
|
|||||||
>
|
>
|
||||||
<div class="bg" @click="close" />
|
<div class="bg" @click="close" />
|
||||||
<div class="window">
|
<div class="window">
|
||||||
<div class="close" @click="close">×</div>
|
<div class="close" @click="close"><img src="@/assets/cross.svg" /></div>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,6 +79,7 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
top: unset;
|
top: unset;
|
||||||
|
padding-bottom: 32px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 16px 16px 0 0;
|
border-radius: 16px 16px 0 0;
|
||||||
transform: translate(0, 0);
|
transform: translate(0, 0);
|
||||||
|
|||||||
53
src/components/SettingsModal.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<Modal ref="modal">
|
||||||
|
<div class="title">Settings</div>
|
||||||
|
<div class="setting">
|
||||||
|
<input type="checkbox" id="reset" v-model="resetAtMidnight">
|
||||||
|
<label for="reset">Reset all tasks to 0 at midnight</label>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState, mapMutations } from 'vuex';
|
||||||
|
|
||||||
|
import Modal from '@/components/Modal.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Modal,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
resetAtMidnight: undefined,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['midnightReset']),
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
resetAtMidnight(newVal) {
|
||||||
|
this.setMidnightReset(newVal);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
this.resetAtMidnight = this.midnightReset;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations(['setMidnightReset']),
|
||||||
|
open() {
|
||||||
|
this.$refs.modal.open();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.setting {
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -19,9 +19,15 @@
|
|||||||
@click="selectCategory(category)"
|
@click="selectCategory(category)"
|
||||||
>
|
>
|
||||||
{{ category }}
|
{{ category }}
|
||||||
<span @click.stop="removeCategory(category)">×</span>
|
<span @click.stop="removeCategory(category)"><img src="@/assets/cross.svg" /></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="settings" @click="openSettings">
|
||||||
|
<img src="@/assets/gear.svg" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SettingsModal ref="settingsModal" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -29,7 +35,12 @@
|
|||||||
import { mapState } from 'vuex';
|
import { mapState } from 'vuex';
|
||||||
import toColor from '@/stringToColor';
|
import toColor from '@/stringToColor';
|
||||||
|
|
||||||
|
import SettingsModal from './SettingsModal.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
SettingsModal,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
newCategory: '+',
|
newCategory: '+',
|
||||||
@@ -68,6 +79,9 @@ export default {
|
|||||||
}
|
}
|
||||||
this.$emit('select', this.selectedCategory);
|
this.$emit('select', this.selectedCategory);
|
||||||
},
|
},
|
||||||
|
openSettings() {
|
||||||
|
this.$refs.settingsModal.open();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
@@ -89,6 +103,7 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 64px;
|
padding: 0 64px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
.add-category {
|
.add-category {
|
||||||
color: $darker;
|
color: $darker;
|
||||||
@@ -106,7 +121,7 @@ export default {
|
|||||||
|
|
||||||
.categories {
|
.categories {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-left: 32px;
|
margin: 0 32px;
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
|
|
||||||
.category {
|
.category {
|
||||||
@@ -118,11 +133,33 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings {
|
||||||
|
position: absolute;
|
||||||
|
right: 64px;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $max-width) {
|
@media screen and (max-width: $max-width) {
|
||||||
.category-bar {
|
.category-bar {
|
||||||
padding: 0 0 0 8px;
|
padding: 0 0 0 8px;
|
||||||
|
|
||||||
|
.categories {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 8px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
.category {
|
.category {
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
|
height: 1rem;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -38,7 +39,10 @@
|
|||||||
color: $darker;
|
color: $darker;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ export default createStore({
|
|||||||
categories: [],
|
categories: [],
|
||||||
tasks: [],
|
tasks: [],
|
||||||
midnightReset: false,
|
midnightReset: false,
|
||||||
lastReset: undefined,
|
lastReset: new Date(),
|
||||||
darkTheme: true,
|
darkTheme: true,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
@@ -77,6 +77,16 @@ export default createStore({
|
|||||||
return newTask;
|
return newTask;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
resetTasks(state) {
|
||||||
|
state.tasks = state.tasks.map((task) => {
|
||||||
|
const newTask = task;
|
||||||
|
newTask.running = false;
|
||||||
|
newTask.totalTime = 0;
|
||||||
|
newTask.startedAt = undefined;
|
||||||
|
return newTask;
|
||||||
|
});
|
||||||
|
state.lastReset = new Date();
|
||||||
|
},
|
||||||
|
|
||||||
setMidnightReset(state, value) {
|
setMidnightReset(state, value) {
|
||||||
state.midnightReset = !!value;
|
state.midnightReset = !!value;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { mapState, mapMutations } from 'vuex';
|
||||||
|
|
||||||
import TheCategoryBar from '@/components/TheCategoryBar.vue';
|
import TheCategoryBar from '@/components/TheCategoryBar.vue';
|
||||||
import TheTaskList from './TheTaskList.vue';
|
import TheTaskList from './TheTaskList.vue';
|
||||||
|
|
||||||
@@ -20,7 +22,18 @@ export default {
|
|||||||
selectedCategory: undefined,
|
selectedCategory: undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['midnightReset', 'lastReset']),
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
const lastMidnight = new Date();
|
||||||
|
lastMidnight.setHours(0, 0, 0, 0);
|
||||||
|
if (this.midnightReset && this.lastReset < lastMidnight) {
|
||||||
|
this.resetTasks();
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
...mapMutations(['resetTasks']),
|
||||||
select(category) {
|
select(category) {
|
||||||
this.selectedCategory = category;
|
this.selectedCategory = category;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
:style="{ background: stringToColor(task.category) }"
|
:style="{ background: stringToColor(task.category) }"
|
||||||
>
|
>
|
||||||
{{ task.category }}
|
{{ task.category }}
|
||||||
<span @click="removeCategory">×</span>
|
<span @click="removeCategory"><img src="@/assets/cross.svg" /></span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="select-category" @click="$refs.categorySelect.open">
|
<div v-else class="select-category" @click="$refs.categorySelect.open">
|
||||||
Assign Category
|
Assign Category
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ module.exports = {
|
|||||||
? '/worktime/'
|
? '/worktime/'
|
||||||
: '/',
|
: '/',
|
||||||
pwa: {
|
pwa: {
|
||||||
|
appleMobileWebAppCapable: 'yes',
|
||||||
|
appleMobileWebAppStatusBarStyle: 'black-transculent',
|
||||||
|
themeColor: '#252525',
|
||||||
workboxOptions: {
|
workboxOptions: {
|
||||||
skipWaiting: true,
|
skipWaiting: true,
|
||||||
clientsClaim: true,
|
clientsClaim: true,
|
||||||
|
|||||||