Skip to content

Commit

Permalink
Some calculation fixes, add progress bar
Browse files Browse the repository at this point in the history
Live update with current time.
Some UI updates.
  • Loading branch information
alinnert committed Dec 7, 2023
1 parent 3fcebe3 commit 88f5f8a
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 46 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Timetracker</title>
</head>
<body>
<body class="sm:bg-gray-100">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
Expand Down
47 changes: 14 additions & 33 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,52 +1,33 @@
<script setup lang="ts">
import { formatDuration } from 'date-fns'
import DaySummary from './components/DaySummary.vue'
import DaysList from './components/DaysList.vue'
import TimestampList from './components/TimestampList.vue'
import ToolbarButton from './components/ToolbarButton.vue'
import UiStack from './components/UiStack.vue'
import UiToolbar from './components/UiToolbar.vue'
import { useTimesStore } from './stores/times'
const timesStore = useTimesStore()
</script>

<template>
<div class="grid grid-cols-1 sm:grid-cols-[1fr,600px,1fr] my-4">
<div class="grid grid-cols-1 sm:grid-cols-[160px,1fr] gap-2 sm:col-start-2">
<div>
<div class="grid grid-cols-1 sm:grid-cols-[1fr,600px,1fr] sm:my-4">
<div
class="grid grid-cols-1 sm:grid-cols-[160px,1fr] gap-2 sm:col-start-2 sm:bg-white p-4 sm:rounded-lg sm:shadow-lg"
>
<div class="row-start-2 sm:row-start-auto">
<DaysList :items="timesStore.allDays"></DaysList>
</div>

<div>
<div class="p-2">
<UiToolbar>
<ToolbarButton @click="timesStore.addCurrentTime()">Zeit hinzufügen</ToolbarButton>
</div>

<TimestampList></TimestampList>
</UiToolbar>

<div class="grid grid-cols-2 gap-2 p-2">
<div>Heute gearbeitet:</div>
<div class="font-bold">
{{ formatDuration(timesStore.workTimeOfSelectedDay, { format: ['hours', 'minutes'] }) }}
</div>
<div>
<template v-if="(timesStore.workTimeOfSelectedDay.hours ?? 0) < 8">
8 Stunden erreicht in:
</template>
<template v-else>Überstunden:</template>
</div>
<div class="font-bold">
{{
formatDuration(timesStore.remainingWorkTimeOfSelectedDay, {
format: ['hours', 'minutes']
})
}}
</div>
<div>Pausenzeit:</div>
<div class="font-bold">
{{
formatDuration(timesStore.breakTimeOfSelectedDay, { format: ['hours', 'minutes'] })
}}
</div>
</div>
<UiStack>
<TimestampList></TimestampList>
<DaySummary></DaySummary>
</UiStack>
</div>
</div>
</div>
Expand Down
47 changes: 47 additions & 0 deletions src/components/DaySummary.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script setup lang="ts">
import { useTimesStore } from '@/stores/times'
import { formatDuration } from 'date-fns'
import { computed } from 'vue'
import { getRatio } from '../lib/date-fns/getRatio'
const timesStore = useTimesStore()
const atLeast8Hours = computed(() => (timesStore.workTimeOfSelectedDay.hours ?? 0) < 8)
const remainingLabel = computed(() =>
atLeast8Hours.value ? '8 Stunden erreicht in:' : 'Überstunden:'
)
</script>

<template>
<div class="grid grid-cols-2 gap-2 px-2">
<div>Heute gearbeitet:</div>
<div class="font-bold">
{{ formatDuration(timesStore.workTimeOfSelectedDay, { format: ['hours', 'minutes'] }) }}
</div>

<div>{{ remainingLabel }}</div>
<div class="font-bold">
{{
formatDuration(timesStore.remainingWorkTimeOfSelectedDay, {
format: ['hours', 'minutes']
})
}}
</div>

<div class="col-span-2">
<progress
class="w-full appearance-none [&::-webkit-progress-bar]:rounded-full [&::-webkit-progress-bar]:h-3 [&::-webkit-progress-bar]:bg-white [&::-webkit-progress-bar]:border-solid [&::-webkit-progress-bar]:border-2 [&::-webkit-progress-bar]:border-teal-600 [&::-webkit-progress-value]:bg-teal-600"
:value="getRatio(timesStore.workTimeOfSelectedDay, { hours: 8 })"
max="1"
></progress>
</div>

<div>Pausenzeit:</div>
<div class="font-bold">
{{
formatDuration(timesStore.breakTimeOfSelectedDay, { format: ['hours', 'minutes'] }) || '-'
}}
</div>
</div>
</template>
2 changes: 1 addition & 1 deletion src/components/DaysList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defineProps<{ items: Date[] }>()
</script>

<template>
<div class="flex flex-col">
<div class="flex flex-col gap-1">
<template v-for="(day, index) in items" :key="index">
<DaysListItem :day="day"></DaysListItem>
</template>
Expand Down
14 changes: 12 additions & 2 deletions src/components/DaysListItem.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
<script setup lang="ts">
import { format } from 'date-fns'
import { useTimesStore } from '@/stores/times'
import { format, isSameDay } from 'date-fns'
defineProps<{ day: Date }>()
const timesStore = useTimesStore()
</script>

<template>
<div class="px-3 py-2 hover:bg-gray-200">
<div
class="px-3 py-2 cursor-default text-center rounded select-none"
:class="{
'hover:bg-gray-200': !isSameDay(timesStore.selectedDay, day),
'bg-gray-300': isSameDay(timesStore.selectedDay, day)
}"
@click="timesStore.setSelectedDay(day)"
>
{{ format(day, 'dd.MM.yyyy') }}
</div>
</template>
8 changes: 4 additions & 4 deletions src/components/TimestampList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ const timesStore = useTimesStore()
</script>

<template>
<div class="grid grid-cols-2 gap-2 p-2">
<div class="grid grid-cols-2 gap-2 px-2">
<template v-for="(timestamp, index) in timesStore.timestampsOfSelectedDay" :key="index">
<div class="grid grid-cols-[1fr,auto] items-center leading-none bg-gray-100">
<div class="px-2">{{ format(timestamp, 'HH:mm') }}</div>
<div class="flex items-center leading-none border border-gray-300 rounded-md p-px">
<div class="flex-grow px-2 text-lg font-mono text-center">{{ format(timestamp, 'HH:mm') }}</div>
<div
class="p-2 bg-red-700 text-white cursor-default"
class="p-0.5 bg-red-700 hover:bg-red-600 active:bg-red-800 text-white cursor-default rounded select-none aspect-square h-full text-center text-lg"
@click="timesStore.removeDate(timestamp)"
>
&times;
Expand Down
2 changes: 1 addition & 1 deletion src/components/ToolbarButton.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<button class="px-3 py-1 bg-gray-100 hover:bg-gray-200 active:bg-gray-300 rounded border border-gray-300 hover:border-gray-400 active:border-gray-400">
<button class="px-3 py-1 bg-gray-100 hover:bg-gray-200 active:bg-gray-300 rounded border border-gray-300 hover:border-gray-400 active:border-gray-400 cursor-default select-none">
<slot></slot>
</button>
</template>
5 changes: 5 additions & 0 deletions src/components/UiStack.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div class="flex flex-col gap-4">
<slot></slot>
</div>
</template>
5 changes: 5 additions & 0 deletions src/components/UiToolbar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div class="mx-2 pb-4 mb-4 border-b border-gray-300">
<slot></slot>
</div>
</template>
8 changes: 8 additions & 0 deletions src/lib/date-fns/getRatio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { add, type Duration } from 'date-fns'

export function getRatio(duration: Duration, compareDuration: Duration): number {
const baseDate = new Date(0)
const compareDate = add(baseDate, compareDuration)
const date = add(baseDate, duration)
return 1 / compareDate.getTime() * date.getTime()
}
19 changes: 15 additions & 4 deletions src/stores/times.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ const storedTimestamps = (

export const useTimesStore = defineStore('times', () => {
const timestamps = ref<Date[]>(storedTimestamps)
const currentTime = ref<Date>(new Date())

setInterval(() => {
currentTime.value = new Date()
}, 1000)

watchEffect(() => {
const serializedTimestamps = JSON.stringify(
Expand Down Expand Up @@ -40,10 +45,12 @@ export const useTimesStore = defineStore('times', () => {
const workTimeOfSelectedDay = computed((): Duration => {
const workTimeDurations: Duration[] = []

timestamps.value.forEach((timestamp, index) => {
const list = [...timestampsOfSelectedDay.value, currentTime.value]

list.forEach((timestamp, index) => {
if (index % 2 === 0) return
workTimeDurations.push(
intervalToDuration({ start: timestamps.value[index - 1], end: timestamp })
intervalToDuration({ start: timestampsOfSelectedDay.value[index - 1], end: timestamp })
)
})

Expand All @@ -53,11 +60,15 @@ export const useTimesStore = defineStore('times', () => {
const breakTimeOfSelectedDay = computed((): Duration => {
const breakTimeDurations: Duration[] = []

timestamps.value.forEach((timestamp, index) => {
const list = isSameDay(timestampsOfSelectedDay.value[0], new Date())
? [...timestampsOfSelectedDay.value, currentTime.value]
: timestampsOfSelectedDay.value

list.forEach((timestamp, index) => {
if (index % 2 === 1 || index === 0) return

breakTimeDurations.push(
intervalToDuration({ start: timestamps.value[index - 1], end: timestamp })
intervalToDuration({ start: timestampsOfSelectedDay.value[index - 1], end: timestamp })
)
})

Expand Down

0 comments on commit 88f5f8a

Please sign in to comment.