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="window">
|
||||
<div class="close" @click="close">×</div>
|
||||
<div class="close" @click="close"><img src="@/assets/cross.svg" /></div>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
@@ -79,6 +79,7 @@ export default {
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
top: unset;
|
||||
padding-bottom: 32px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 16px 16px 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)"
|
||||
>
|
||||
{{ category }}
|
||||
<span @click.stop="removeCategory(category)">×</span>
|
||||
<span @click.stop="removeCategory(category)"><img src="@/assets/cross.svg" /></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings" @click="openSettings">
|
||||
<img src="@/assets/gear.svg" />
|
||||
</div>
|
||||
|
||||
<SettingsModal ref="settingsModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,7 +35,12 @@
|
||||
import { mapState } from 'vuex';
|
||||
import toColor from '@/stringToColor';
|
||||
|
||||
import SettingsModal from './SettingsModal.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SettingsModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newCategory: '+',
|
||||
@@ -68,6 +79,9 @@ export default {
|
||||
}
|
||||
this.$emit('select', this.selectedCategory);
|
||||
},
|
||||
openSettings() {
|
||||
this.$refs.settingsModal.open();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -89,6 +103,7 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 64px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.add-category {
|
||||
color: $darker;
|
||||
@@ -106,7 +121,7 @@ export default {
|
||||
|
||||
.categories {
|
||||
display: flex;
|
||||
margin-left: 32px;
|
||||
margin: 0 32px;
|
||||
overflow-x: scroll;
|
||||
|
||||
.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) {
|
||||
.category-bar {
|
||||
padding: 0 0 0 8px;
|
||||
|
||||
.categories {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.settings {
|
||||
position: fixed;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
.category {
|
||||
border-radius: 16px;
|
||||
height: 1rem;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
@@ -38,7 +39,10 @@
|
||||
color: $darker;
|
||||
|
||||
span {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export default createStore({
|
||||
categories: [],
|
||||
tasks: [],
|
||||
midnightReset: false,
|
||||
lastReset: undefined,
|
||||
lastReset: new Date(),
|
||||
darkTheme: true,
|
||||
},
|
||||
mutations: {
|
||||
@@ -77,6 +77,16 @@ export default createStore({
|
||||
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) {
|
||||
state.midnightReset = !!value;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex';
|
||||
|
||||
import TheCategoryBar from '@/components/TheCategoryBar.vue';
|
||||
import TheTaskList from './TheTaskList.vue';
|
||||
|
||||
@@ -20,7 +22,18 @@ export default {
|
||||
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: {
|
||||
...mapMutations(['resetTasks']),
|
||||
select(category) {
|
||||
this.selectedCategory = category;
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
:style="{ background: stringToColor(task.category) }"
|
||||
>
|
||||
{{ task.category }}
|
||||
<span @click="removeCategory">×</span>
|
||||
<span @click="removeCategory"><img src="@/assets/cross.svg" /></span>
|
||||
</div>
|
||||
<div v-else class="select-category" @click="$refs.categorySelect.open">
|
||||
Assign Category
|
||||
|
||||
@@ -3,6 +3,9 @@ module.exports = {
|
||||
? '/worktime/'
|
||||
: '/',
|
||||
pwa: {
|
||||
appleMobileWebAppCapable: 'yes',
|
||||
appleMobileWebAppStatusBarStyle: 'black-transculent',
|
||||
themeColor: '#252525',
|
||||
workboxOptions: {
|
||||
skipWaiting: true,
|
||||
clientsClaim: true,
|
||||
|
||||