Fixed a number of issues
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
initialRunningEntry: { startTime: number; description: string | null; clientId: string; categoryId: string } | null;
|
||||
initialRunningEntry: {
|
||||
startTime: number;
|
||||
description: string | null;
|
||||
clientId: string;
|
||||
categoryId: string;
|
||||
} | null;
|
||||
clients: { id: string; name: string }[];
|
||||
categories: { id: string; name: string; color: string | null }[];
|
||||
tags: { id: string; name: string; color: string | null }[];
|
||||
@@ -11,9 +16,9 @@ const props = defineProps<{
|
||||
const isRunning = ref(false);
|
||||
const startTime = ref<number | null>(null);
|
||||
const elapsedTime = ref(0);
|
||||
const description = ref('');
|
||||
const selectedClientId = ref('');
|
||||
const selectedCategoryId = ref('');
|
||||
const description = ref("");
|
||||
const selectedClientId = ref("");
|
||||
const selectedCategoryId = ref("");
|
||||
const selectedTags = ref<string[]>([]);
|
||||
let interval: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
@@ -22,21 +27,24 @@ function formatTime(ms: number) {
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
|
||||
const timeStr = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
|
||||
|
||||
const timeStr = `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
||||
|
||||
// Calculate rounded version
|
||||
const totalMinutes = Math.round(ms / 1000 / 60);
|
||||
const roundedHours = Math.floor(totalMinutes / 60);
|
||||
const roundedMinutes = totalMinutes % 60;
|
||||
|
||||
let roundedStr = '';
|
||||
|
||||
let roundedStr = "";
|
||||
if (roundedHours > 0) {
|
||||
roundedStr = roundedMinutes > 0 ? `${roundedHours}h ${roundedMinutes}m` : `${roundedHours}h`;
|
||||
roundedStr =
|
||||
roundedMinutes > 0
|
||||
? `${roundedHours}h ${roundedMinutes}m`
|
||||
: `${roundedHours}h`;
|
||||
} else {
|
||||
roundedStr = `${roundedMinutes}m`;
|
||||
}
|
||||
|
||||
|
||||
return `${timeStr} (${roundedStr})`;
|
||||
}
|
||||
|
||||
@@ -53,7 +61,7 @@ onMounted(() => {
|
||||
if (props.initialRunningEntry) {
|
||||
isRunning.value = true;
|
||||
startTime.value = props.initialRunningEntry.startTime;
|
||||
description.value = props.initialRunningEntry.description || '';
|
||||
description.value = props.initialRunningEntry.description || "";
|
||||
selectedClientId.value = props.initialRunningEntry.clientId;
|
||||
selectedCategoryId.value = props.initialRunningEntry.categoryId;
|
||||
elapsedTime.value = Date.now() - startTime.value;
|
||||
@@ -69,26 +77,26 @@ onUnmounted(() => {
|
||||
|
||||
async function startTimer() {
|
||||
if (!selectedClientId.value) {
|
||||
alert('Please select a client');
|
||||
alert("Please select a client");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedCategoryId.value) {
|
||||
alert('Please select a category');
|
||||
alert("Please select a category");
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await fetch('/api/time-entries/start', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
const res = await fetch("/api/time-entries/start", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
description: description.value,
|
||||
clientId: selectedClientId.value,
|
||||
categoryId: selectedCategoryId.value,
|
||||
tags: selectedTags.value,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
startTime.value = new Date(data.startTime).getTime();
|
||||
@@ -100,8 +108,8 @@ async function startTimer() {
|
||||
}
|
||||
|
||||
async function stopTimer() {
|
||||
const res = await fetch('/api/time-entries/stop', {
|
||||
method: 'POST',
|
||||
const res = await fetch("/api/time-entries/stop", {
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
@@ -109,9 +117,9 @@ async function stopTimer() {
|
||||
if (interval) clearInterval(interval);
|
||||
elapsedTime.value = 0;
|
||||
startTime.value = null;
|
||||
description.value = '';
|
||||
selectedClientId.value = '';
|
||||
selectedCategoryId.value = '';
|
||||
description.value = "";
|
||||
selectedClientId.value = "";
|
||||
selectedCategoryId.value = "";
|
||||
selectedTags.value = [];
|
||||
window.location.reload();
|
||||
}
|
||||
@@ -127,13 +135,17 @@ async function stopTimer() {
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Client</span>
|
||||
</label>
|
||||
<select
|
||||
v-model="selectedClientId"
|
||||
<select
|
||||
v-model="selectedClientId"
|
||||
class="select select-bordered w-full"
|
||||
:disabled="isRunning"
|
||||
>
|
||||
<option value="">Select a client...</option>
|
||||
<option v-for="client in clients" :key="client.id" :value="client.id">
|
||||
<option
|
||||
v-for="client in clients"
|
||||
:key="client.id"
|
||||
:value="client.id"
|
||||
>
|
||||
{{ client.name }}
|
||||
</option>
|
||||
</select>
|
||||
@@ -143,13 +155,17 @@ async function stopTimer() {
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Category</span>
|
||||
</label>
|
||||
<select
|
||||
v-model="selectedCategoryId"
|
||||
<select
|
||||
v-model="selectedCategoryId"
|
||||
class="select select-bordered w-full"
|
||||
:disabled="isRunning"
|
||||
>
|
||||
<option value="">Select a category...</option>
|
||||
<option v-for="category in categories" :key="category.id" :value="category.id">
|
||||
<option
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:value="category.id"
|
||||
>
|
||||
{{ category.name }}
|
||||
</option>
|
||||
</select>
|
||||
@@ -161,11 +177,11 @@ async function stopTimer() {
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Description</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="description"
|
||||
type="text"
|
||||
placeholder="What are you working on?"
|
||||
class="input input-bordered w-full"
|
||||
<input
|
||||
v-model="description"
|
||||
type="text"
|
||||
placeholder="What are you working on?"
|
||||
class="input input-bordered w-full"
|
||||
:disabled="isRunning"
|
||||
/>
|
||||
</div>
|
||||
@@ -182,7 +198,7 @@ async function stopTimer() {
|
||||
@click="toggleTag(tag.id)"
|
||||
:class="[
|
||||
'badge badge-lg cursor-pointer transition-all',
|
||||
selectedTags.includes(tag.id) ? 'badge-primary' : 'badge-outline'
|
||||
selectedTags.includes(tag.id) ? 'badge-primary' : 'badge-outline',
|
||||
]"
|
||||
:disabled="isRunning"
|
||||
type="button"
|
||||
@@ -194,21 +210,19 @@ async function stopTimer() {
|
||||
|
||||
<!-- Timer and Action Row -->
|
||||
<div class="flex flex-col sm:flex-row items-center gap-6 pt-4">
|
||||
<div class="font-mono text-5xl font-bold tabular-nums tracking-tight text-center sm:text-left flex-grow">
|
||||
<div
|
||||
class="font-mono text-5xl font-bold tabular-nums tracking-tight text-center sm:text-left grow"
|
||||
>
|
||||
{{ formatTime(elapsedTime) }}
|
||||
</div>
|
||||
<button
|
||||
v-if="!isRunning"
|
||||
@click="startTimer"
|
||||
<button
|
||||
v-if="!isRunning"
|
||||
@click="startTimer"
|
||||
class="btn btn-primary btn-lg min-w-40"
|
||||
>
|
||||
▶️ Start Timer
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
@click="stopTimer"
|
||||
class="btn btn-error btn-lg min-w-40"
|
||||
>
|
||||
<button v-else @click="stopTimer" class="btn btn-error btn-lg min-w-40">
|
||||
⏹️ Stop Timer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user