OOOOOPS
All checks were successful
Docker Deploy / build-and-push (push) Successful in 3m56s

This commit is contained in:
2026-01-19 23:39:00 -07:00
parent effc6ac37e
commit 54cac49b70
22 changed files with 350 additions and 280 deletions

View File

@@ -211,11 +211,11 @@ function clearForm() {
<!-- Client and Category Row --> <!-- Client and Category Row -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="manual-client">
<span class="label-text font-medium">Client</span> Client <span class="label-text-alt text-error">*</span>
<span class="label-text-alt text-error">*</span>
</label> </label>
<select <select
id="manual-client"
v-model="selectedClientId" v-model="selectedClientId"
class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors"
:disabled="isSubmitting" :disabled="isSubmitting"
@@ -232,11 +232,11 @@ function clearForm() {
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="manual-category">
<span class="label-text font-medium">Category</span> Category <span class="label-text-alt text-error">*</span>
<span class="label-text-alt text-error">*</span>
</label> </label>
<select <select
id="manual-category"
v-model="selectedCategoryId" v-model="selectedCategoryId"
class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors"
:disabled="isSubmitting" :disabled="isSubmitting"
@@ -256,11 +256,11 @@ function clearForm() {
<!-- Start Date and Time --> <!-- Start Date and Time -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="manual-start-date">
<span class="label-text font-medium">Start Date</span> Start Date <span class="label-text-alt text-error">*</span>
<span class="label-text-alt text-error">*</span>
</label> </label>
<input <input
id="manual-start-date"
v-model="startDate" v-model="startDate"
type="date" type="date"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors"
@@ -269,11 +269,11 @@ function clearForm() {
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="manual-start-time">
<span class="label-text font-medium">Start Time</span> Start Time <span class="label-text-alt text-error">*</span>
<span class="label-text-alt text-error">*</span>
</label> </label>
<input <input
id="manual-start-time"
v-model="startTime" v-model="startTime"
type="time" type="time"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors"
@@ -285,11 +285,11 @@ function clearForm() {
<!-- End Date and Time --> <!-- End Date and Time -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="manual-end-date">
<span class="label-text font-medium">End Date</span> End Date <span class="label-text-alt text-error">*</span>
<span class="label-text-alt text-error">*</span>
</label> </label>
<input <input
id="manual-end-date"
v-model="endDate" v-model="endDate"
type="date" type="date"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors"
@@ -298,11 +298,11 @@ function clearForm() {
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="manual-end-time">
<span class="label-text font-medium">End Time</span> End Time <span class="label-text-alt text-error">*</span>
<span class="label-text-alt text-error">*</span>
</label> </label>
<input <input
id="manual-end-time"
v-model="endTime" v-model="endTime"
type="time" type="time"
class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="input input-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors"
@@ -313,10 +313,11 @@ function clearForm() {
<!-- Description Row --> <!-- Description Row -->
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="manual-description">
<span class="label-text font-medium">Description</span> Description
</label> </label>
<input <input
id="manual-description"
v-model="description" v-model="description"
type="text" type="text"
placeholder="What did you work on?" placeholder="What did you work on?"
@@ -327,9 +328,7 @@ function clearForm() {
<!-- Tags Section --> <!-- Tags Section -->
<div v-if="tags.length > 0" class="form-control"> <div v-if="tags.length > 0" class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="manual-tags"> Tags </label>
<span class="label-text font-medium">Tags</span>
</label>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<button <button
v-for="tag in tags" v-for="tag in tags"

View File

@@ -134,10 +134,11 @@ async function stopTimer() {
<!-- Client and Description Row --> <!-- Client and Description Row -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="timer-client">
<span class="label-text font-medium">Client</span> Client
</label> </label>
<select <select
id="timer-client"
v-model="selectedClientId" v-model="selectedClientId"
class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors"
:disabled="isRunning" :disabled="isRunning"
@@ -154,10 +155,11 @@ async function stopTimer() {
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="timer-category">
<span class="label-text font-medium">Category</span> Category
</label> </label>
<select <select
id="timer-category"
v-model="selectedCategoryId" v-model="selectedCategoryId"
class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors" class="select select-bordered w-full bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors"
:disabled="isRunning" :disabled="isRunning"
@@ -176,10 +178,11 @@ async function stopTimer() {
<!-- Description Row --> <!-- Description Row -->
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="timer-description">
<span class="label-text font-medium">Description</span> Description
</label> </label>
<input <input
id="timer-description"
v-model="description" v-model="description"
type="text" type="text"
placeholder="What are you working on?" placeholder="What are you working on?"
@@ -190,9 +193,7 @@ async function stopTimer() {
<!-- Tags Section --> <!-- Tags Section -->
<div v-if="tags.length > 0" class="form-control"> <div v-if="tags.length > 0" class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="timer-tags"> Tags </label>
<span class="label-text font-medium">Tags</span>
</label>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<button <button
v-for="tag in tags" v-for="tag in tags"

View File

@@ -182,11 +182,12 @@ function closeShowTokenModal() {
<form @submit.prevent="createToken" class="space-y-4"> <form @submit.prevent="createToken" class="space-y-4">
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label class="label pb-2 font-medium" for="token-name">
<span class="label-text font-medium">Token Name</span> Token Name
</label> </label>
<input <input
type="text" type="text"
id="token-name"
v-model="newTokenName" v-model="newTokenName"
placeholder="e.g. CI/CD Pipeline" placeholder="e.g. CI/CD Pipeline"
class="input input-bordered w-full" class="input input-bordered w-full"

View File

@@ -1,21 +1,24 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from "vue";
import { Icon } from '@iconify/vue'; import { Icon } from "@iconify/vue";
const currentPassword = ref(''); const currentPassword = ref("");
const newPassword = ref(''); const newPassword = ref("");
const confirmPassword = ref(''); const confirmPassword = ref("");
const loading = ref(false); const loading = ref(false);
const message = ref<{ type: 'success' | 'error'; text: string } | null>(null); const message = ref<{ type: "success" | "error"; text: string } | null>(null);
async function changePassword() { async function changePassword() {
if (newPassword.value !== confirmPassword.value) { if (newPassword.value !== confirmPassword.value) {
message.value = { type: 'error', text: 'New passwords do not match' }; message.value = { type: "error", text: "New passwords do not match" };
return; return;
} }
if (newPassword.value.length < 8) { if (newPassword.value.length < 8) {
message.value = { type: 'error', text: 'Password must be at least 8 characters' }; message.value = {
type: "error",
text: "Password must be at least 8 characters",
};
return; return;
} }
@@ -23,10 +26,10 @@ async function changePassword() {
message.value = null; message.value = null;
try { try {
const response = await fetch('/api/user/change-password', { const response = await fetch("/api/user/change-password", {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
currentPassword: currentPassword.value, currentPassword: currentPassword.value,
@@ -36,20 +39,26 @@ async function changePassword() {
}); });
if (response.ok) { if (response.ok) {
message.value = { type: 'success', text: 'Password changed successfully!' }; message.value = {
currentPassword.value = ''; type: "success",
newPassword.value = ''; text: "Password changed successfully!",
confirmPassword.value = ''; };
currentPassword.value = "";
newPassword.value = "";
confirmPassword.value = "";
setTimeout(() => { setTimeout(() => {
message.value = null; message.value = null;
}, 3000); }, 3000);
} else { } else {
const data = await response.json().catch(() => ({})); const data = await response.json().catch(() => ({}));
message.value = { type: 'error', text: data.error || 'Failed to change password' }; message.value = {
type: "error",
text: data.error || "Failed to change password",
};
} }
} catch (error) { } catch (error) {
message.value = { type: 'error', text: 'An error occurred' }; message.value = { type: "error", text: "An error occurred" };
} finally { } finally {
loading.value = false; loading.value = false;
} }
@@ -59,8 +68,21 @@ async function changePassword() {
<template> <template>
<div> <div>
<!-- Success/Error Message Display --> <!-- Success/Error Message Display -->
<div v-if="message" :class="['alert mb-6', message.type === 'success' ? 'alert-success' : 'alert-error']"> <div
<Icon :icon="message.type === 'success' ? 'heroicons:check-circle' : 'heroicons:exclamation-circle'" class="w-6 h-6 shrink-0" /> v-if="message"
:class="[
'alert mb-6',
message.type === 'success' ? 'alert-success' : 'alert-error',
]"
>
<Icon
:icon="
message.type === 'success'
? 'heroicons:check-circle'
: 'heroicons:exclamation-circle'
"
class="w-6 h-6 shrink-0"
/>
<span>{{ message.text }}</span> <span>{{ message.text }}</span>
</div> </div>
@@ -73,11 +95,15 @@ async function changePassword() {
<form @submit.prevent="changePassword" class="space-y-5"> <form @submit.prevent="changePassword" class="space-y-5">
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label
<span class="label-text font-medium text-sm sm:text-base">Current Password</span> class="label pb-2 font-medium text-sm sm:text-base"
for="current-password"
>
Current Password
</label> </label>
<input <input
type="password" type="password"
id="current-password"
v-model="currentPassword" v-model="currentPassword"
placeholder="Enter current password" placeholder="Enter current password"
class="input input-bordered w-full" class="input input-bordered w-full"
@@ -86,11 +112,15 @@ async function changePassword() {
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label
<span class="label-text font-medium text-sm sm:text-base">New Password</span> class="label pb-2 font-medium text-sm sm:text-base"
for="new-password"
>
New Password
</label> </label>
<input <input
type="password" type="password"
id="new-password"
v-model="newPassword" v-model="newPassword"
placeholder="Enter new password" placeholder="Enter new password"
class="input input-bordered w-full" class="input input-bordered w-full"
@@ -98,16 +128,23 @@ async function changePassword() {
minlength="8" minlength="8"
/> />
<div class="label pt-2"> <div class="label pt-2">
<span class="label-text-alt text-base-content/60 text-xs sm:text-sm">Minimum 8 characters</span> <span
class="label-text-alt text-base-content/60 text-xs sm:text-sm"
>Minimum 8 characters</span
>
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label
<span class="label-text font-medium text-sm sm:text-base">Confirm New Password</span> class="label pb-2 font-medium text-sm sm:text-base"
for="confirm-password"
>
Confirm New Password
</label> </label>
<input <input
type="password" type="password"
id="confirm-password"
v-model="confirmPassword" v-model="confirmPassword"
placeholder="Confirm new password" placeholder="Confirm new password"
class="input input-bordered w-full" class="input input-bordered w-full"
@@ -117,8 +154,15 @@ async function changePassword() {
</div> </div>
<div class="flex justify-end pt-4"> <div class="flex justify-end pt-4">
<button type="submit" class="btn btn-primary w-full sm:w-auto" :disabled="loading"> <button
<span v-if="loading" class="loading loading-spinner loading-sm"></span> type="submit"
class="btn btn-primary w-full sm:w-auto"
:disabled="loading"
>
<span
v-if="loading"
class="loading loading-spinner loading-sm"
></span>
<Icon v-else icon="heroicons:lock-closed" class="w-5 h-5" /> <Icon v-else icon="heroicons:lock-closed" class="w-5 h-5" />
Update Password Update Password
</button> </button>

View File

@@ -80,13 +80,15 @@ async function updateProfile() {
<form @submit.prevent="updateProfile" class="space-y-5"> <form @submit.prevent="updateProfile" class="space-y-5">
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label
<span class="label-text font-medium text-sm sm:text-base" class="label pb-2 font-medium text-sm sm:text-base"
>Full Name</span for="profile-name"
> >
Full Name
</label> </label>
<input <input
type="text" type="text"
id="profile-name"
v-model="name" v-model="name"
placeholder="Your full name" placeholder="Your full name"
class="input input-bordered w-full" class="input input-bordered w-full"
@@ -95,13 +97,15 @@ async function updateProfile() {
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2"> <label
<span class="label-text font-medium text-sm sm:text-base" class="label pb-2 font-medium text-sm sm:text-base"
>Email</span for="profile-email"
> >
Email
</label> </label>
<input <input
type="email" type="email"
id="profile-email"
:value="props.user.email" :value="props.user.email"
class="input input-bordered w-full" class="input input-bordered w-full"
disabled disabled

View File

@@ -40,18 +40,18 @@ const allUsers = await db.select().from(users).all();
<form method="POST" action="/api/admin/settings"> <form method="POST" action="/api/admin/settings">
<div class="form-control"> <div class="form-control">
<label class="label cursor-pointer items-start gap-4"> <label for="registration_enabled" class="label pb-2 font-medium text-sm sm:text-base">
<span class="label-text flex-1 min-w-0"> Allow New Registrations
<div class="font-semibold">Allow New Registrations</div> </label>
<div class="text-sm text-gray-500">When disabled, only existing users can log in</div>
</span> <br>
<input <input
type="checkbox" type="checkbox"
name="registration_enabled" name="registration_enabled"
class="toggle toggle-primary shrink-0 mt-1" class="toggle toggle-primary shrink-0 mt-1"
checked={registrationEnabled} checked={registrationEnabled}
/> />
</label>
</div> </div>
<div class="card-actions justify-end mt-6"> <div class="card-actions justify-end mt-6">

View File

@@ -54,8 +54,8 @@ if (!category) return Astro.redirect('/dashboard/categories');
<input type="hidden" name="redirectTo" value="/dashboard/categories" /> <input type="hidden" name="redirectTo" value="/dashboard/categories" />
<div class="form-control"> <div class="form-control">
<label class="label pb-2" for="name"> <label class="label pb-2 font-medium" for="name">
<span class="label-text font-medium">Category Name</span> Category Name
</label> </label>
<input <input
type="text" type="text"
@@ -69,8 +69,8 @@ if (!category) return Astro.redirect('/dashboard/categories');
</div> </div>
<div class="form-control mt-4"> <div class="form-control mt-4">
<label class="label pb-2" for="color"> <label class="label pb-2 font-medium" for="color">
<span class="label-text font-medium">Color (optional)</span> Color (optional)
</label> </label>
<input <input
type="color" type="color"

View File

@@ -19,8 +19,8 @@ if (!user) return Astro.redirect('/login');
<input type="hidden" name="redirectTo" value="/dashboard/categories" /> <input type="hidden" name="redirectTo" value="/dashboard/categories" />
<div class="card-body"> <div class="card-body">
<div class="form-control"> <div class="form-control">
<label class="label pb-2" for="name"> <label class="label pb-2 font-medium" for="name">
<span class="label-text font-medium">Category Name</span> Category Name
</label> </label>
<input <input
type="text" type="text"
@@ -33,8 +33,8 @@ if (!user) return Astro.redirect('/login');
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2" for="color"> <label class="label pb-2 font-medium" for="color">
<span class="label-text font-medium">Color (optional)</span> Color (optional)
</label> </label>
<input <input
type="color" type="color"

View File

@@ -50,7 +50,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
<div class="card-body"> <div class="card-body">
<div class="form-control"> <div class="form-control">
<label class="label" for="name"> <label class="label" for="name">
<span class="label-text">Client Name</span> Client Name
</label> </label>
<input <input
type="text" type="text"
@@ -65,7 +65,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
<div class="form-control"> <div class="form-control">
<label class="label" for="email"> <label class="label" for="email">
<span class="label-text">Email (optional)</span> Email (optional)
</label> </label>
<input <input
type="email" type="email"
@@ -79,7 +79,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
<div class="form-control"> <div class="form-control">
<label class="label" for="phone"> <label class="label" for="phone">
<span class="label-text">Phone (optional)</span> Phone (optional)
</label> </label>
<input <input
type="tel" type="tel"
@@ -95,7 +95,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
<div class="form-control"> <div class="form-control">
<label class="label" for="street"> <label class="label" for="street">
<span class="label-text">Street Address (optional)</span> Street Address (optional)
</label> </label>
<input <input
type="text" type="text"
@@ -110,7 +110,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-control"> <div class="form-control">
<label class="label" for="city"> <label class="label" for="city">
<span class="label-text">City (optional)</span> City (optional)
</label> </label>
<input <input
type="text" type="text"
@@ -124,7 +124,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
<div class="form-control"> <div class="form-control">
<label class="label" for="state"> <label class="label" for="state">
<span class="label-text">State / Province (optional)</span> State / Province (optional)
</label> </label>
<input <input
type="text" type="text"
@@ -140,7 +140,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-control"> <div class="form-control">
<label class="label" for="zip"> <label class="label" for="zip">
<span class="label-text">Zip / Postal Code (optional)</span> Zip / Postal Code (optional)
</label> </label>
<input <input
type="text" type="text"
@@ -154,7 +154,7 @@ if (!client) return Astro.redirect('/dashboard/clients');
<div class="form-control"> <div class="form-control">
<label class="label" for="country"> <label class="label" for="country">
<span class="label-text">Country (optional)</span> Country (optional)
</label> </label>
<input <input
type="text" type="text"

View File

@@ -13,7 +13,7 @@ if (!user) return Astro.redirect('/login');
<div class="card-body"> <div class="card-body">
<div class="form-control"> <div class="form-control">
<label class="label" for="name"> <label class="label" for="name">
<span class="label-text">Client Name</span> Client Name
</label> </label>
<input <input
type="text" type="text"
@@ -27,7 +27,7 @@ if (!user) return Astro.redirect('/login');
<div class="form-control"> <div class="form-control">
<label class="label" for="email"> <label class="label" for="email">
<span class="label-text">Email (optional)</span> Email (optional)
</label> </label>
<input <input
type="email" type="email"
@@ -40,7 +40,7 @@ if (!user) return Astro.redirect('/login');
<div class="form-control"> <div class="form-control">
<label class="label" for="phone"> <label class="label" for="phone">
<span class="label-text">Phone (optional)</span> Phone (optional)
</label> </label>
<input <input
type="tel" type="tel"
@@ -55,7 +55,7 @@ if (!user) return Astro.redirect('/login');
<div class="form-control"> <div class="form-control">
<label class="label" for="street"> <label class="label" for="street">
<span class="label-text">Street Address (optional)</span> Street Address (optional)
</label> </label>
<input <input
type="text" type="text"
@@ -69,7 +69,7 @@ if (!user) return Astro.redirect('/login');
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-control"> <div class="form-control">
<label class="label" for="city"> <label class="label" for="city">
<span class="label-text">City (optional)</span> City (optional)
</label> </label>
<input <input
type="text" type="text"
@@ -82,7 +82,7 @@ if (!user) return Astro.redirect('/login');
<div class="form-control"> <div class="form-control">
<label class="label" for="state"> <label class="label" for="state">
<span class="label-text">State / Province (optional)</span> State / Province (optional)
</label> </label>
<input <input
type="text" type="text"
@@ -97,7 +97,7 @@ if (!user) return Astro.redirect('/login');
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-control"> <div class="form-control">
<label class="label" for="zip"> <label class="label" for="zip">
<span class="label-text">Zip / Postal Code (optional)</span> Zip / Postal Code (optional)
</label> </label>
<input <input
type="text" type="text"
@@ -110,7 +110,7 @@ if (!user) return Astro.redirect('/login');
<div class="form-control"> <div class="form-control">
<label class="label" for="country"> <label class="label" for="country">
<span class="label-text">Country (optional)</span> Country (optional)
</label> </label>
<input <input
type="text" type="text"

View File

@@ -267,16 +267,16 @@ const isDraft = invoice.status === 'draft';
<h4 class="text-sm font-bold mb-3">Add Item</h4> <h4 class="text-sm font-bold mb-3">Add Item</h4>
<div class="grid grid-cols-1 sm:grid-cols-12 gap-4 items-end"> <div class="grid grid-cols-1 sm:grid-cols-12 gap-4 items-end">
<div class="sm:col-span-6"> <div class="sm:col-span-6">
<label class="label label-text text-xs pt-0">Description</label> <label class="label text-xs pt-0" for="item-description">Description</label>
<input type="text" name="description" class="input input-sm input-bordered w-full" required placeholder="Service or product..." /> <input type="text" id="item-description" name="description" class="input input-sm input-bordered w-full" required placeholder="Service or product..." />
</div> </div>
<div class="sm:col-span-2"> <div class="sm:col-span-2">
<label class="label label-text text-xs pt-0">Qty</label> <label class="label text-xs pt-0" for="item-quantity">Qty</label>
<input type="number" name="quantity" step="0.01" class="input input-sm input-bordered w-full" required value="1" /> <input type="number" id="item-quantity" name="quantity" step="0.01" class="input input-sm input-bordered w-full" required value="1" />
</div> </div>
<div class="sm:col-span-3"> <div class="sm:col-span-3">
<label class="label label-text text-xs pt-0">Unit Price ({invoice.currency})</label> <label class="label text-xs pt-0" for="item-unit-price">Unit Price ({invoice.currency})</label>
<input type="number" name="unitPrice" step="0.01" class="input input-sm input-bordered w-full" required placeholder="0.00" /> <input type="number" id="item-unit-price" name="unitPrice" step="0.01" class="input input-sm input-bordered w-full" required placeholder="0.00" />
</div> </div>
<div class="sm:col-span-1"> <div class="sm:col-span-1">
<button type="submit" class="btn btn-sm btn-primary w-full"> <button type="submit" class="btn btn-sm btn-primary w-full">
@@ -349,11 +349,12 @@ const isDraft = invoice.status === 'draft';
<p class="py-4">Enter the tax percentage to apply to the subtotal.</p> <p class="py-4">Enter the tax percentage to apply to the subtotal.</p>
<form method="POST" action={`/api/invoices/${invoice.id}/update-tax`}> <form method="POST" action={`/api/invoices/${invoice.id}/update-tax`}>
<div class="form-control mb-6"> <div class="form-control mb-6">
<label class="label"> <label class="label" for="tax-rate">
<span class="label-text">Tax Rate (%)</span> Tax Rate (%)
</label> </label>
<input <input
type="number" type="number"
id="tax-rate"
name="taxRate" name="taxRate"
step="0.01" step="0.01"
min="0" min="0"

View File

@@ -60,11 +60,12 @@ const discountValueDisplay = invoice.discountType === 'fixed'
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Number --> <!-- Number -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-number">
<span class="label-text font-semibold">Number</span> Number
</label> </label>
<input <input
type="text" type="text"
id="invoice-number"
name="number" name="number"
class="input input-bordered font-mono" class="input input-bordered font-mono"
value={invoice.number} value={invoice.number}
@@ -74,10 +75,10 @@ const discountValueDisplay = invoice.discountType === 'fixed'
<!-- Currency --> <!-- Currency -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-currency">
<span class="label-text font-semibold">Currency</span> Currency
</label> </label>
<select name="currency" class="select select-bordered w-full"> <select id="invoice-currency" name="currency" class="select select-bordered w-full">
<option value="USD" selected={invoice.currency === 'USD'}>USD ($)</option> <option value="USD" selected={invoice.currency === 'USD'}>USD ($)</option>
<option value="EUR" selected={invoice.currency === 'EUR'}>EUR (€)</option> <option value="EUR" selected={invoice.currency === 'EUR'}>EUR (€)</option>
<option value="GBP" selected={invoice.currency === 'GBP'}>GBP (£)</option> <option value="GBP" selected={invoice.currency === 'GBP'}>GBP (£)</option>
@@ -88,11 +89,12 @@ const discountValueDisplay = invoice.discountType === 'fixed'
<!-- Issue Date --> <!-- Issue Date -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-issue-date">
<span class="label-text font-semibold">Issue Date</span> Issue Date
</label> </label>
<input <input
type="date" type="date"
id="invoice-issue-date"
name="issueDate" name="issueDate"
class="input input-bordered" class="input input-bordered"
value={issueDateStr} value={issueDateStr}
@@ -102,13 +104,12 @@ const discountValueDisplay = invoice.discountType === 'fixed'
<!-- Due Date --> <!-- Due Date -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-due-date">
<span class="label-text font-semibold">
{invoice.type === 'quote' ? 'Valid Until' : 'Due Date'} {invoice.type === 'quote' ? 'Valid Until' : 'Due Date'}
</span>
</label> </label>
<input <input
type="date" type="date"
id="invoice-due-date"
name="dueDate" name="dueDate"
class="input input-bordered" class="input input-bordered"
value={dueDateStr} value={dueDateStr}
@@ -118,16 +119,17 @@ const discountValueDisplay = invoice.discountType === 'fixed'
<!-- Discount --> <!-- Discount -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-discount-type">
<span class="label-text font-semibold">Discount</span> Discount
</label> </label>
<div class="join w-full"> <div class="join w-full">
<select name="discountType" class="select select-bordered join-item"> <select id="invoice-discount-type" name="discountType" class="select select-bordered join-item">
<option value="percentage" selected={!invoice.discountType || invoice.discountType === 'percentage'}>%</option> <option value="percentage" selected={!invoice.discountType || invoice.discountType === 'percentage'}>%</option>
<option value="fixed" selected={invoice.discountType === 'fixed'}>Fixed</option> <option value="fixed" selected={invoice.discountType === 'fixed'}>Fixed</option>
</select> </select>
<input <input
type="number" type="number"
id="invoice-discount-value"
name="discountValue" name="discountValue"
step="0.01" step="0.01"
min="0" min="0"
@@ -139,11 +141,12 @@ const discountValueDisplay = invoice.discountType === 'fixed'
<!-- Tax Rate --> <!-- Tax Rate -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-tax-rate">
<span class="label-text font-semibold">Tax Rate (%)</span> Tax Rate (%)
</label> </label>
<input <input
type="number" type="number"
id="invoice-tax-rate"
name="taxRate" name="taxRate"
step="0.01" step="0.01"
min="0" min="0"
@@ -156,10 +159,11 @@ const discountValueDisplay = invoice.discountType === 'fixed'
<!-- Notes --> <!-- Notes -->
<div class="form-control flex flex-col"> <div class="form-control flex flex-col">
<label class="label"> <label class="label font-semibold" for="invoice-notes">
<span class="label-text font-semibold">Notes / Terms</span> Notes / Terms
</label> </label>
<textarea <textarea
id="invoice-notes"
name="notes" name="notes"
class="textarea textarea-bordered h-32 font-mono text-sm" class="textarea textarea-bordered h-32 font-mono text-sm"
placeholder="Payment terms, bank details, or thank you notes..." placeholder="Payment terms, bank details, or thank you notes..."

View File

@@ -108,17 +108,17 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
<!-- Document Type --> <!-- Document Type -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="document-type-invoice">
<span class="label-text font-semibold">Document Type</span> Document Type
</label> </label>
<div class="flex gap-4"> <div class="flex gap-4">
<label class="label cursor-pointer justify-start gap-2 border border-base-300 rounded-lg p-3 flex-1 hover:border-primary has-checked:border-primary has-checked:bg-primary/5 transition-all"> <label class="label cursor-pointer justify-start gap-2 border border-base-300 rounded-lg p-3 flex-1 hover:border-primary has-checked:border-primary has-checked:bg-primary/5 transition-all font-medium" for="document-type-invoice">
<input type="radio" name="type" value="invoice" class="radio radio-primary" checked /> <input type="radio" id="document-type-invoice" name="type" value="invoice" class="radio radio-primary" checked />
<span class="label-text font-medium">Invoice</span> Invoice
</label> </label>
<label class="label cursor-pointer justify-start gap-2 border border-base-300 rounded-lg p-3 flex-1 hover:border-primary has-checked:border-primary has-checked:bg-primary/5 transition-all"> <label class="label cursor-pointer justify-start gap-2 border border-base-300 rounded-lg p-3 flex-1 hover:border-primary has-checked:border-primary has-checked:bg-primary/5 transition-all font-medium" for="document-type-quote">
<input type="radio" name="type" value="quote" class="radio radio-primary" /> <input type="radio" id="document-type-quote" name="type" value="quote" class="radio radio-primary" />
<span class="label-text font-medium">Quote / Estimate</span> Quote / Estimate
</label> </label>
</div> </div>
</div> </div>
@@ -126,10 +126,10 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Client --> <!-- Client -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-client">
<span class="label-text font-semibold">Client</span> Client
</label> </label>
<select name="clientId" class="select select-bordered w-full" required> <select id="invoice-client" name="clientId" class="select select-bordered w-full" required>
<option value="" disabled selected>Select a client...</option> <option value="" disabled selected>Select a client...</option>
{teamClients.map(client => ( {teamClients.map(client => (
<option value={client.id}>{client.name}</option> <option value={client.id}>{client.name}</option>
@@ -139,8 +139,8 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
<!-- Number --> <!-- Number -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="documentNumber">
<span class="label-text font-semibold">Number</span> Number
</label> </label>
<input <input
type="text" type="text"
@@ -156,11 +156,12 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
<!-- Issue Date --> <!-- Issue Date -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-issue-date">
<span class="label-text font-semibold">Issue Date</span> Issue Date
</label> </label>
<input <input
type="date" type="date"
id="invoice-issue-date"
name="issueDate" name="issueDate"
class="input input-bordered" class="input input-bordered"
value={today} value={today}
@@ -170,11 +171,12 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
<!-- Due Date --> <!-- Due Date -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-due-date" id="dueDateLabel">
<span class="label-text font-semibold" id="dueDateLabel">Due Date</span> Due Date
</label> </label>
<input <input
type="date" type="date"
id="invoice-due-date"
name="dueDate" name="dueDate"
class="input input-bordered" class="input input-bordered"
value={defaultDueDate} value={defaultDueDate}
@@ -184,10 +186,10 @@ const defaultDueDate = nextMonth.toISOString().split('T')[0];
<!-- Currency --> <!-- Currency -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-semibold" for="invoice-currency">
<span class="label-text font-semibold">Currency</span> Currency
</label> </label>
<select name="currency" class="select select-bordered w-full"> <select id="invoice-currency" name="currency" class="select select-bordered w-full">
<option value="USD">USD ($)</option> <option value="USD">USD ($)</option>
<option value="EUR">EUR (€)</option> <option value="EUR">EUR (€)</option>
<option value="GBP">GBP (£)</option> <option value="GBP">GBP (£)</option>

View File

@@ -26,8 +26,8 @@ if (!user) return Astro.redirect('/login');
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2" for="name"> <label class="label pb-2 font-medium" for="name">
<span class="label-text font-medium">Team Name</span> Team Name
</label> </label>
<input <input
type="text" type="text"

View File

@@ -276,10 +276,10 @@ function getTimeRangeLabel(range: string) {
<div class="card-body"> <div class="card-body">
<form method="GET" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> <form method="GET" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="reports-range">
<span class="label-text font-medium">Time Range</span> Time Range
</label> </label>
<select name="range" class="select select-bordered" onchange="this.form.submit()"> <select id="reports-range" name="range" class="select select-bordered" onchange="this.form.submit()">
<option value="today" selected={timeRange === 'today'}>Today</option> <option value="today" selected={timeRange === 'today'}>Today</option>
<option value="week" selected={timeRange === 'week'}>Last 7 Days</option> <option value="week" selected={timeRange === 'week'}>Last 7 Days</option>
<option value="month" selected={timeRange === 'month'}>Last 30 Days</option> <option value="month" selected={timeRange === 'month'}>Last 30 Days</option>
@@ -293,11 +293,12 @@ function getTimeRangeLabel(range: string) {
{timeRange === 'custom' && ( {timeRange === 'custom' && (
<> <>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="reports-from">
<span class="label-text font-medium">From Date</span> From Date
</label> </label>
<input <input
type="date" type="date"
id="reports-from"
name="from" name="from"
class="input input-bordered w-full" class="input input-bordered w-full"
value={customFrom || (startDate.getFullYear() + '-' + String(startDate.getMonth() + 1).padStart(2, '0') + '-' + String(startDate.getDate()).padStart(2, '0'))} value={customFrom || (startDate.getFullYear() + '-' + String(startDate.getMonth() + 1).padStart(2, '0') + '-' + String(startDate.getDate()).padStart(2, '0'))}
@@ -305,11 +306,12 @@ function getTimeRangeLabel(range: string) {
/> />
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="reports-to">
<span class="label-text font-medium">To Date</span> To Date
</label> </label>
<input <input
type="date" type="date"
id="reports-to"
name="to" name="to"
class="input input-bordered w-full" class="input input-bordered w-full"
value={customTo || (endDate.getFullYear() + '-' + String(endDate.getMonth() + 1).padStart(2, '0') + '-' + String(endDate.getDate()).padStart(2, '0'))} value={customTo || (endDate.getFullYear() + '-' + String(endDate.getMonth() + 1).padStart(2, '0') + '-' + String(endDate.getDate()).padStart(2, '0'))}
@@ -320,10 +322,10 @@ function getTimeRangeLabel(range: string) {
)} )}
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="reports-member">
<span class="label-text font-medium">Team Member</span> Team Member
</label> </label>
<select name="member" class="select select-bordered" onchange="this.form.submit()"> <select id="reports-member" name="member" class="select select-bordered" onchange="this.form.submit()">
<option value="">All Members</option> <option value="">All Members</option>
{teamMembers.map(member => ( {teamMembers.map(member => (
<option value={member.id} selected={selectedMemberId === member.id}> <option value={member.id} selected={selectedMemberId === member.id}>
@@ -334,10 +336,10 @@ function getTimeRangeLabel(range: string) {
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="reports-category">
<span class="label-text font-medium">Category</span> Category
</label> </label>
<select name="category" class="select select-bordered" onchange="this.form.submit()"> <select id="reports-category" name="category" class="select select-bordered" onchange="this.form.submit()">
<option value="">All Categories</option> <option value="">All Categories</option>
{allCategories.map(category => ( {allCategories.map(category => (
<option value={category.id} selected={selectedCategoryId === category.id}> <option value={category.id} selected={selectedCategoryId === category.id}>
@@ -348,10 +350,10 @@ function getTimeRangeLabel(range: string) {
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="reports-client">
<span class="label-text font-medium">Client</span> Client
</label> </label>
<select name="client" class="select select-bordered" onchange="this.form.submit()"> <select id="reports-client" name="client" class="select select-bordered" onchange="this.form.submit()">
<option value="">All Clients</option> <option value="">All Clients</option>
{allClients.map(client => ( {allClients.map(client => (
<option value={client.id} selected={selectedClientId === client.id}> <option value={client.id} selected={selectedClientId === client.id}>

View File

@@ -40,7 +40,7 @@ if (!isAdmin) return Astro.redirect('/dashboard/team');
<div class="form-control"> <div class="form-control">
<label class="label" for="email"> <label class="label" for="email">
<span class="label-text">Email Address</span> Email Address
</label> </label>
<input <input
type="email" type="email"
@@ -54,7 +54,7 @@ if (!isAdmin) return Astro.redirect('/dashboard/team');
<div class="form-control"> <div class="form-control">
<label class="label" for="role"> <label class="label" for="role">
<span class="label-text">Role</span> Role
</label> </label>
<select id="role" name="role" class="select select-bordered" required> <select id="role" name="role" class="select select-bordered" required>
<option value="member">Member</option> <option value="member">Member</option>

View File

@@ -112,12 +112,13 @@ const successType = url.searchParams.get('success');
</div> </div>
</div> </div>
<label class="form-control"> <div class="form-control">
<div class="label"> <label class="label font-medium" for="team-name">
<span class="label-text font-medium">Team Name</span> Team Name
</div> </label>
<input <input
type="text" type="text"
id="team-name"
name="name" name="name"
value={organization.name} value={organization.name}
placeholder="Organization name" placeholder="Organization name"
@@ -127,77 +128,82 @@ const successType = url.searchParams.get('success');
<div class="label"> <div class="label">
<span class="label-text-alt text-base-content/60">This name is visible to all team members</span> <span class="label-text-alt text-base-content/60">This name is visible to all team members</span>
</div> </div>
</label> </div>
<div class="divider">Address Information</div> <div class="divider">Address Information</div>
<label class="form-control"> <div class="form-control">
<div class="label"> <label class="label font-medium" for="team-street">
<span class="label-text font-medium">Street Address</span> Street Address
</div> </label>
<input <input
type="text" type="text"
id="team-street"
name="street" name="street"
value={organization.street || ''} value={organization.street || ''}
placeholder="123 Main Street" placeholder="123 Main Street"
class="input input-bordered w-full" class="input input-bordered w-full"
/> />
</label> </div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<label class="form-control"> <div class="form-control">
<div class="label"> <label class="label font-medium" for="team-city">
<span class="label-text font-medium">City</span> City
</div> </label>
<input <input
type="text" type="text"
id="team-city"
name="city" name="city"
value={organization.city || ''} value={organization.city || ''}
placeholder="City" placeholder="City"
class="input input-bordered w-full" class="input input-bordered w-full"
/> />
</label>
<label class="form-control">
<div class="label">
<span class="label-text font-medium">State/Province</span>
</div> </div>
<div class="form-control">
<label class="label font-medium" for="team-state">
State/Province
</label>
<input <input
type="text" type="text"
id="team-state"
name="state" name="state"
value={organization.state || ''} value={organization.state || ''}
placeholder="State/Province" placeholder="State/Province"
class="input input-bordered w-full" class="input input-bordered w-full"
/> />
</label> </div>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<label class="form-control"> <div class="form-control">
<div class="label"> <label class="label font-medium" for="team-zip">
<span class="label-text font-medium">Postal Code</span> Postal Code
</div> </label>
<input <input
type="text" type="text"
id="team-zip"
name="zip" name="zip"
value={organization.zip || ''} value={organization.zip || ''}
placeholder="12345" placeholder="12345"
class="input input-bordered w-full" class="input input-bordered w-full"
/> />
</label>
<label class="form-control">
<div class="label">
<span class="label-text font-medium">Country</span>
</div> </div>
<div class="form-control">
<label class="label font-medium" for="team-country">
Country
</label>
<input <input
type="text" type="text"
id="team-country"
name="country" name="country"
value={organization.country || ''} value={organization.country || ''}
placeholder="Country" placeholder="Country"
class="input input-bordered w-full" class="input input-bordered w-full"
/> />
</label> </div>
</div> </div>
<div class="flex flex-col sm:flex-row justify-between items-center gap-4 mt-6"> <div class="flex flex-col sm:flex-row justify-between items-center gap-4 mt-6">

View File

@@ -51,8 +51,8 @@ if (!category) return Astro.redirect('/dashboard/team/settings');
<form method="POST" action={`/api/categories/${id}/update`} class="card bg-base-200 shadow-xl border border-base-300"> <form method="POST" action={`/api/categories/${id}/update`} class="card bg-base-200 shadow-xl border border-base-300">
<div class="card-body"> <div class="card-body">
<div class="form-control"> <div class="form-control">
<label class="label pb-2" for="name"> <label class="label pb-2 font-medium" for="name">
<span class="label-text font-medium">Category Name</span> Category Name
</label> </label>
<input <input
type="text" type="text"
@@ -66,8 +66,8 @@ if (!category) return Astro.redirect('/dashboard/team/settings');
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2" for="color"> <label class="label pb-2 font-medium" for="color">
<span class="label-text font-medium">Color (optional)</span> Color (optional)
</label> </label>
<input <input
type="color" type="color"

View File

@@ -18,8 +18,8 @@ if (!user) return Astro.redirect('/login');
<form method="POST" action="/api/categories/create" class="card bg-base-200 shadow-xl border border-base-300"> <form method="POST" action="/api/categories/create" class="card bg-base-200 shadow-xl border border-base-300">
<div class="card-body"> <div class="card-body">
<div class="form-control"> <div class="form-control">
<label class="label pb-2" for="name"> <label class="label pb-2 font-medium" for="name">
<span class="label-text font-medium">Category Name</span> Category Name
</label> </label>
<input <input
type="text" type="text"
@@ -32,8 +32,8 @@ if (!user) return Astro.redirect('/login');
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label pb-2" for="color"> <label class="label pb-2 font-medium" for="color">
<span class="label-text font-medium">Color (optional)</span> Color (optional)
</label> </label>
<input <input
type="color" type="color"

View File

@@ -225,11 +225,12 @@ const paginationPages = getPaginationPages(page, totalPages);
<div class="card-body"> <div class="card-body">
<form method="GET" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-4"> <form method="GET" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-4">
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="tracker-search">
<span class="label-text font-medium">Search</span> Search
</label> </label>
<input <input
type="text" type="text"
id="tracker-search"
name="search" name="search"
placeholder="Search descriptions..." placeholder="Search descriptions..."
class="input input-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" class="input input-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full"
@@ -238,10 +239,10 @@ const paginationPages = getPaginationPages(page, totalPages);
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="tracker-client">
<span class="label-text font-medium">Client</span> Client
</label> </label>
<select name="client" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()"> <select id="tracker-client" name="client" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()">
<option value="">All Clients</option> <option value="">All Clients</option>
{allClients.map(client => ( {allClients.map(client => (
<option value={client.id} selected={filterClient === client.id}> <option value={client.id} selected={filterClient === client.id}>
@@ -252,10 +253,10 @@ const paginationPages = getPaginationPages(page, totalPages);
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="tracker-category">
<span class="label-text font-medium">Category</span> Category
</label> </label>
<select name="category" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()"> <select id="tracker-category" name="category" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()">
<option value="">All Categories</option> <option value="">All Categories</option>
{allCategories.map(category => ( {allCategories.map(category => (
<option value={category.id} selected={filterCategory === category.id}> <option value={category.id} selected={filterCategory === category.id}>
@@ -266,10 +267,10 @@ const paginationPages = getPaginationPages(page, totalPages);
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="tracker-status">
<span class="label-text font-medium">Status</span> Status
</label> </label>
<select name="status" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()"> <select id="tracker-status" name="status" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()">
<option value="" selected={filterStatus === ''}>All Entries</option> <option value="" selected={filterStatus === ''}>All Entries</option>
<option value="completed" selected={filterStatus === 'completed'}>Completed</option> <option value="completed" selected={filterStatus === 'completed'}>Completed</option>
<option value="running" selected={filterStatus === 'running'}>Running</option> <option value="running" selected={filterStatus === 'running'}>Running</option>
@@ -277,10 +278,10 @@ const paginationPages = getPaginationPages(page, totalPages);
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="tracker-type">
<span class="label-text font-medium">Entry Type</span> Entry Type
</label> </label>
<select name="type" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()"> <select id="tracker-type" name="type" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()">
<option value="" selected={filterType === ''}>All Types</option> <option value="" selected={filterType === ''}>All Types</option>
<option value="timed" selected={filterType === 'timed'}>Timed</option> <option value="timed" selected={filterType === 'timed'}>Timed</option>
<option value="manual" selected={filterType === 'manual'}>Manual</option> <option value="manual" selected={filterType === 'manual'}>Manual</option>
@@ -288,10 +289,10 @@ const paginationPages = getPaginationPages(page, totalPages);
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label font-medium" for="tracker-sort">
<span class="label-text font-medium">Sort By</span> Sort By
</label> </label>
<select name="sort" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()"> <select id="tracker-sort" name="sort" class="select select-bordered bg-base-300/50 hover:bg-base-300 focus:bg-base-300 border-base-300/50 focus:border-primary transition-colors w-full" onchange="this.form.submit()">
<option value="start-desc" selected={sortBy === 'start-desc'}>Newest First</option> <option value="start-desc" selected={sortBy === 'start-desc'}>Newest First</option>
<option value="start-asc" selected={sortBy === 'start-asc'}>Oldest First</option> <option value="start-asc" selected={sortBy === 'start-asc'}>Oldest First</option>
<option value="duration-desc" selected={sortBy === 'duration-desc'}>Longest Duration</option> <option value="duration-desc" selected={sortBy === 'duration-desc'}>Longest Duration</option>

View File

@@ -32,31 +32,33 @@ const errorMessage =
)} )}
<form action="/api/auth/login" method="POST" class="space-y-4"> <form action="/api/auth/login" method="POST" class="space-y-4">
<label class="form-control"> <div class="form-control">
<div class="label"> <label class="label font-medium" for="email">
<span class="label-text font-medium">Email</span> Email
</div> </label>
<input <input
type="email" type="email"
id="email"
name="email" name="email"
placeholder="your@email.com" placeholder="your@email.com"
class="input input-bordered w-full" class="input input-bordered w-full"
required required
/> />
</label>
<label class="form-control">
<div class="label">
<span class="label-text font-medium">Password</span>
</div> </div>
<div class="form-control">
<label class="label font-medium" for="password">
Password
</label>
<input <input
type="password" type="password"
id="password"
name="password" name="password"
placeholder="Enter your password" placeholder="Enter your password"
class="input input-bordered w-full" class="input input-bordered w-full"
required required
/> />
</label> </div>
<button class="btn btn-primary w-full mt-6">Sign In</button> <button class="btn btn-primary w-full mt-6">Sign In</button>
</form> </form>

View File

@@ -64,44 +64,47 @@ const errorMessage =
) : ( ) : (
<> <>
<form action="/api/auth/signup" method="POST" class="space-y-4"> <form action="/api/auth/signup" method="POST" class="space-y-4">
<label class="form-control"> <div class="form-control">
<div class="label"> <label class="label font-medium" for="name">
<span class="label-text font-medium">Full Name</span> Full Name
</div> </label>
<input <input
type="text" type="text"
id="name"
name="name" name="name"
placeholder="John Doe" placeholder="John Doe"
class="input input-bordered w-full" class="input input-bordered w-full"
required required
/> />
</label>
<label class="form-control">
<div class="label">
<span class="label-text font-medium">Email</span>
</div> </div>
<div class="form-control">
<label class="label font-medium" for="email">
Email
</label>
<input <input
type="email" type="email"
id="email"
name="email" name="email"
placeholder="your@email.com" placeholder="your@email.com"
class="input input-bordered w-full" class="input input-bordered w-full"
required required
/> />
</label>
<label class="form-control">
<div class="label">
<span class="label-text font-medium">Password</span>
</div> </div>
<div class="form-control">
<label class="label font-medium" for="password">
Password
</label>
<input <input
type="password" type="password"
id="password"
name="password" name="password"
placeholder="Create a strong password" placeholder="Create a strong password"
class="input input-bordered w-full" class="input input-bordered w-full"
required required
/> />
</label> </div>
<button class="btn btn-primary w-full mt-6">Create Account</button> <button class="btn btn-primary w-full mt-6">Create Account</button>
</form> </form>