mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-16 12:12:15 +01:00
add date selector to time entries, fixes ST-134
This commit is contained in:
@@ -183,7 +183,7 @@ test('test that adding a new tag to an existing time entry works', async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Test that Start / End Time Update Works
|
// Test that Start / End Time Update Works
|
||||||
test('test that updating a the start of an existing time entry in the overview works on blur', async ({
|
test('test that updating a the start of an existing time entry in the overview works on enter', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
await goToTimeOverview(page);
|
await goToTimeOverview(page);
|
||||||
@@ -220,7 +220,7 @@ test('test that updating a the start of an existing time entry in the overview w
|
|||||||
page
|
page
|
||||||
.getByTestId('time_entry_range_end')
|
.getByTestId('time_entry_range_end')
|
||||||
.getByTestId('time_picker_minute')
|
.getByTestId('time_picker_minute')
|
||||||
.press('Tab'),
|
.press('Enter'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { getLocalizedDayJs } from '@/utils/time';
|
import { getLocalizedDayJs } from '@/utils/time';
|
||||||
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: string;
|
||||||
|
}>();
|
||||||
const model = defineModel<string | null>({
|
const model = defineModel<string | null>({
|
||||||
default: null,
|
default: null,
|
||||||
});
|
});
|
||||||
@@ -19,6 +23,7 @@ function updateDate(event: Event) {
|
|||||||
.set('date', newDate.date())
|
.set('date', newDate.date())
|
||||||
.utc()
|
.utc()
|
||||||
.format();
|
.format();
|
||||||
|
emit('changed', model.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +33,8 @@ function updateTempValue(event: Event) {
|
|||||||
const target = event.target as HTMLInputElement;
|
const target = event.target as HTMLInputElement;
|
||||||
tempDate.value = target.value;
|
tempDate.value = target.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits(['changed']);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -36,7 +43,13 @@ function updateTempValue(event: Event) {
|
|||||||
ref="datePicker"
|
ref="datePicker"
|
||||||
@change="updateTempValue"
|
@change="updateTempValue"
|
||||||
@blur="updateDate"
|
@blur="updateDate"
|
||||||
class="bg-input-background border text-white border-input-border rounded-md"
|
@keydown.enter="updateDate"
|
||||||
|
:class="
|
||||||
|
twMerge(
|
||||||
|
'bg-input-background border text-white border-input-border rounded-md',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
"
|
||||||
type="date"
|
type="date"
|
||||||
id="start"
|
id="start"
|
||||||
name="trip-start"
|
name="trip-start"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { getDayJsInstance, getLocalizedDayJs } from '@/utils/time';
|
import { getLocalizedDayJs } from '@/utils/time';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
const model = defineModel<string | null>({
|
const model = defineModel<string | null>({
|
||||||
@@ -47,11 +47,6 @@ function updateHours(event: Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits(['changed']);
|
const emit = defineEmits(['changed']);
|
||||||
onMounted(() => {
|
|
||||||
if (!model.value) {
|
|
||||||
model.value = getDayJsInstance().utc().format();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -86,7 +81,7 @@ onMounted(() => {
|
|||||||
type="text"
|
type="text"
|
||||||
:class="
|
:class="
|
||||||
twMerge(
|
twMerge(
|
||||||
'border-none bg-transparent px-1 py-0.5 w-[30px] text-center focus:ring-0 focus:bg-card-background-active',
|
'border-none bg-transparent px-1 py-1 w-[30px] text-center focus:ring-0 focus:bg-card-background-active',
|
||||||
props.size === 'large' ? 'text-base' : 'text-sm'
|
props.size === 'large' ? 'text-base' : 'text-sm'
|
||||||
)
|
)
|
||||||
" />
|
" />
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { defineProps, ref, watch } from 'vue';
|
import { defineProps, ref, watch } from 'vue';
|
||||||
import TimePicker from '@/Components/Common/TimePicker.vue';
|
import TimePicker from '@/Components/Common/TimePicker.vue';
|
||||||
import { useFocusWithin } from '@vueuse/core';
|
import { useFocusWithin } from '@vueuse/core';
|
||||||
|
import DatePicker from '@/Components/Common/DatePicker.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
start: string;
|
start: string;
|
||||||
@@ -35,21 +36,32 @@ watch(focused, (newValue, oldValue) => {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
ref="dropdownContent"
|
ref="dropdownContent"
|
||||||
class="grid grid-cols-2 divide-x divide-card-background-separator text-center py-1">
|
class="grid grid-cols-2 divide-x divide-card-background-separator text-center py-2">
|
||||||
<div>
|
<div class="px-2">
|
||||||
<div class="font-bold text-white text-sm pb-1">Start</div>
|
<div class="font-bold text-white text-sm pb-2">Start</div>
|
||||||
<TimePicker
|
<div class="space-y-1">
|
||||||
data-testid="time_entry_range_start"
|
<TimePicker
|
||||||
@changed="updateTimeEntry"
|
data-testid="time_entry_range_start"
|
||||||
v-model="tempStart"></TimePicker>
|
@changed="updateTimeEntry"
|
||||||
|
v-model="tempStart"></TimePicker>
|
||||||
|
<DatePicker
|
||||||
|
class="text-sm px-2 py-1"
|
||||||
|
@changed="updateTimeEntry"
|
||||||
|
v-model="tempStart"></DatePicker>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="px-2">
|
||||||
<div class="font-bold text-white text-sm pb-1">End</div>
|
<div class="font-bold text-white text-sm pb-2">End</div>
|
||||||
<TimePicker
|
<div v-if="tempEnd !== null" class="space-y-1">
|
||||||
v-if="tempEnd !== null"
|
<TimePicker
|
||||||
data-testid="time_entry_range_end"
|
data-testid="time_entry_range_end"
|
||||||
@changed="updateTimeEntry"
|
@changed="updateTimeEntry"
|
||||||
v-model="tempEnd"></TimePicker>
|
v-model="tempEnd"></TimePicker>
|
||||||
|
<DatePicker
|
||||||
|
class="text-sm px-2 py-1"
|
||||||
|
@changed="updateTimeEntry"
|
||||||
|
v-model="tempEnd"></DatePicker>
|
||||||
|
</div>
|
||||||
<div class="text-muted" v-else>-- : --</div>
|
<div class="text-muted" v-else>-- : --</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import dayjs from 'dayjs';
|
|||||||
import parse from 'parse-duration';
|
import parse from 'parse-duration';
|
||||||
import { useCurrentTimeEntryStore } from '@/utils/useCurrentTimeEntry';
|
import { useCurrentTimeEntryStore } from '@/utils/useCurrentTimeEntry';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getDayJsInstance } from '@/utils/time';
|
import { formatDuration, getDayJsInstance } from '@/utils/time';
|
||||||
const currentTimeEntryStore = useCurrentTimeEntryStore();
|
const currentTimeEntryStore = useCurrentTimeEntryStore();
|
||||||
const { startLiveTimer, stopLiveTimer, updateTimer, startTimer } =
|
const { startLiveTimer, stopLiveTimer, updateTimer, startTimer } =
|
||||||
currentTimeEntryStore;
|
currentTimeEntryStore;
|
||||||
@@ -34,8 +34,8 @@ const currentTime = computed({
|
|||||||
}
|
}
|
||||||
if (now.value && currentTimeEntry.value.start) {
|
if (now.value && currentTimeEntry.value.start) {
|
||||||
const startTime = dayjs(currentTimeEntry.value.start);
|
const startTime = dayjs(currentTimeEntry.value.start);
|
||||||
const diff = now.value.diff(startTime);
|
const diff = now.value.diff(startTime, 'seconds');
|
||||||
return dayjs(diff).utc().format('HH:mm:ss');
|
return formatDuration(diff);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -46,6 +46,14 @@ export function formatHumanReadableDuration(duration: number): string {
|
|||||||
return `${hours}h ${minutes.toString().padStart(2, '0')}min`;
|
return `${hours}h ${minutes.toString().padStart(2, '0')}min`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatDuration(duration: number): string {
|
||||||
|
const dayJsDuration = dayjs.duration(duration, 's');
|
||||||
|
const hours = dayJsDuration.hours() + dayJsDuration.days() * 24;
|
||||||
|
const minutes = dayJsDuration.minutes();
|
||||||
|
const seconds = dayJsDuration.seconds();
|
||||||
|
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function calculateDifference(start: string, end: string | null) {
|
export function calculateDifference(start: string, end: string | null) {
|
||||||
if (end === null) {
|
if (end === null) {
|
||||||
end = dayjs().utc().format();
|
end = dayjs().utc().format();
|
||||||
|
|||||||
Reference in New Issue
Block a user