This commit is contained in:
@@ -211,11 +211,11 @@ function clearForm() {
|
||||
<!-- Client and Category Row -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Client</span>
|
||||
<span class="label-text-alt text-error">*</span>
|
||||
<label class="label pb-2 font-medium" for="manual-client">
|
||||
Client <span class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
<select
|
||||
id="manual-client"
|
||||
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"
|
||||
:disabled="isSubmitting"
|
||||
@@ -232,11 +232,11 @@ function clearForm() {
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Category</span>
|
||||
<span class="label-text-alt text-error">*</span>
|
||||
<label class="label pb-2 font-medium" for="manual-category">
|
||||
Category <span class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
<select
|
||||
id="manual-category"
|
||||
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"
|
||||
:disabled="isSubmitting"
|
||||
@@ -256,11 +256,11 @@ function clearForm() {
|
||||
<!-- Start Date and Time -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Start Date</span>
|
||||
<span class="label-text-alt text-error">*</span>
|
||||
<label class="label pb-2 font-medium" for="manual-start-date">
|
||||
Start Date <span class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="manual-start-date"
|
||||
v-model="startDate"
|
||||
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"
|
||||
@@ -269,11 +269,11 @@ function clearForm() {
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Start Time</span>
|
||||
<span class="label-text-alt text-error">*</span>
|
||||
<label class="label pb-2 font-medium" for="manual-start-time">
|
||||
Start Time <span class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="manual-start-time"
|
||||
v-model="startTime"
|
||||
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"
|
||||
@@ -285,11 +285,11 @@ function clearForm() {
|
||||
<!-- End Date and Time -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">End Date</span>
|
||||
<span class="label-text-alt text-error">*</span>
|
||||
<label class="label pb-2 font-medium" for="manual-end-date">
|
||||
End Date <span class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="manual-end-date"
|
||||
v-model="endDate"
|
||||
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"
|
||||
@@ -298,11 +298,11 @@ function clearForm() {
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">End Time</span>
|
||||
<span class="label-text-alt text-error">*</span>
|
||||
<label class="label pb-2 font-medium" for="manual-end-time">
|
||||
End Time <span class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="manual-end-time"
|
||||
v-model="endTime"
|
||||
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"
|
||||
@@ -313,10 +313,11 @@ function clearForm() {
|
||||
|
||||
<!-- Description Row -->
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Description</span>
|
||||
<label class="label pb-2 font-medium" for="manual-description">
|
||||
Description
|
||||
</label>
|
||||
<input
|
||||
id="manual-description"
|
||||
v-model="description"
|
||||
type="text"
|
||||
placeholder="What did you work on?"
|
||||
@@ -327,9 +328,7 @@ function clearForm() {
|
||||
|
||||
<!-- Tags Section -->
|
||||
<div v-if="tags.length > 0" class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Tags</span>
|
||||
</label>
|
||||
<label class="label pb-2 font-medium" for="manual-tags"> Tags </label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
v-for="tag in tags"
|
||||
|
||||
@@ -134,10 +134,11 @@ async function stopTimer() {
|
||||
<!-- Client and Description Row -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Client</span>
|
||||
<label class="label pb-2 font-medium" for="timer-client">
|
||||
Client
|
||||
</label>
|
||||
<select
|
||||
id="timer-client"
|
||||
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"
|
||||
:disabled="isRunning"
|
||||
@@ -154,10 +155,11 @@ async function stopTimer() {
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Category</span>
|
||||
<label class="label pb-2 font-medium" for="timer-category">
|
||||
Category
|
||||
</label>
|
||||
<select
|
||||
id="timer-category"
|
||||
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"
|
||||
:disabled="isRunning"
|
||||
@@ -176,10 +178,11 @@ async function stopTimer() {
|
||||
|
||||
<!-- Description Row -->
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Description</span>
|
||||
<label class="label pb-2 font-medium" for="timer-description">
|
||||
Description
|
||||
</label>
|
||||
<input
|
||||
id="timer-description"
|
||||
v-model="description"
|
||||
type="text"
|
||||
placeholder="What are you working on?"
|
||||
@@ -190,9 +193,7 @@ async function stopTimer() {
|
||||
|
||||
<!-- Tags Section -->
|
||||
<div v-if="tags.length > 0" class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Tags</span>
|
||||
</label>
|
||||
<label class="label pb-2 font-medium" for="timer-tags"> Tags </label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
v-for="tag in tags"
|
||||
|
||||
@@ -182,11 +182,12 @@ function closeShowTokenModal() {
|
||||
|
||||
<form @submit.prevent="createToken" class="space-y-4">
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium">Token Name</span>
|
||||
<label class="label pb-2 font-medium" for="token-name">
|
||||
Token Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="token-name"
|
||||
v-model="newTokenName"
|
||||
placeholder="e.g. CI/CD Pipeline"
|
||||
class="input input-bordered w-full"
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { ref } from "vue";
|
||||
import { Icon } from "@iconify/vue";
|
||||
|
||||
const currentPassword = ref('');
|
||||
const newPassword = ref('');
|
||||
const confirmPassword = ref('');
|
||||
const currentPassword = ref("");
|
||||
const newPassword = ref("");
|
||||
const confirmPassword = ref("");
|
||||
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() {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -23,10 +26,10 @@ async function changePassword() {
|
||||
message.value = null;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/user/change-password', {
|
||||
method: 'POST',
|
||||
const response = await fetch("/api/user/change-password", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
currentPassword: currentPassword.value,
|
||||
@@ -36,20 +39,26 @@ async function changePassword() {
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
message.value = { type: 'success', text: 'Password changed successfully!' };
|
||||
currentPassword.value = '';
|
||||
newPassword.value = '';
|
||||
confirmPassword.value = '';
|
||||
message.value = {
|
||||
type: "success",
|
||||
text: "Password changed successfully!",
|
||||
};
|
||||
currentPassword.value = "";
|
||||
newPassword.value = "";
|
||||
confirmPassword.value = "";
|
||||
|
||||
setTimeout(() => {
|
||||
message.value = null;
|
||||
}, 3000);
|
||||
} else {
|
||||
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) {
|
||||
message.value = { type: 'error', text: 'An error occurred' };
|
||||
message.value = { type: "error", text: "An error occurred" };
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -59,8 +68,21 @@ async function changePassword() {
|
||||
<template>
|
||||
<div>
|
||||
<!-- Success/Error Message Display -->
|
||||
<div 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" />
|
||||
<div
|
||||
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>
|
||||
</div>
|
||||
|
||||
@@ -73,11 +95,15 @@ async function changePassword() {
|
||||
|
||||
<form @submit.prevent="changePassword" class="space-y-5">
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium text-sm sm:text-base">Current Password</span>
|
||||
<label
|
||||
class="label pb-2 font-medium text-sm sm:text-base"
|
||||
for="current-password"
|
||||
>
|
||||
Current Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="current-password"
|
||||
v-model="currentPassword"
|
||||
placeholder="Enter current password"
|
||||
class="input input-bordered w-full"
|
||||
@@ -86,11 +112,15 @@ async function changePassword() {
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium text-sm sm:text-base">New Password</span>
|
||||
<label
|
||||
class="label pb-2 font-medium text-sm sm:text-base"
|
||||
for="new-password"
|
||||
>
|
||||
New Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="new-password"
|
||||
v-model="newPassword"
|
||||
placeholder="Enter new password"
|
||||
class="input input-bordered w-full"
|
||||
@@ -98,16 +128,23 @@ async function changePassword() {
|
||||
minlength="8"
|
||||
/>
|
||||
<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 class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium text-sm sm:text-base">Confirm New Password</span>
|
||||
<label
|
||||
class="label pb-2 font-medium text-sm sm:text-base"
|
||||
for="confirm-password"
|
||||
>
|
||||
Confirm New Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="confirm-password"
|
||||
v-model="confirmPassword"
|
||||
placeholder="Confirm new password"
|
||||
class="input input-bordered w-full"
|
||||
@@ -117,8 +154,15 @@ async function changePassword() {
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-4">
|
||||
<button type="submit" class="btn btn-primary w-full sm:w-auto" :disabled="loading">
|
||||
<span v-if="loading" class="loading loading-spinner loading-sm"></span>
|
||||
<button
|
||||
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" />
|
||||
Update Password
|
||||
</button>
|
||||
|
||||
@@ -80,13 +80,15 @@ async function updateProfile() {
|
||||
|
||||
<form @submit.prevent="updateProfile" class="space-y-5">
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium text-sm sm:text-base"
|
||||
>Full Name</span
|
||||
>
|
||||
<label
|
||||
class="label pb-2 font-medium text-sm sm:text-base"
|
||||
for="profile-name"
|
||||
>
|
||||
Full Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="profile-name"
|
||||
v-model="name"
|
||||
placeholder="Your full name"
|
||||
class="input input-bordered w-full"
|
||||
@@ -95,13 +97,15 @@ async function updateProfile() {
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-medium text-sm sm:text-base"
|
||||
>Email</span
|
||||
>
|
||||
<label
|
||||
class="label pb-2 font-medium text-sm sm:text-base"
|
||||
for="profile-email"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="profile-email"
|
||||
:value="props.user.email"
|
||||
class="input input-bordered w-full"
|
||||
disabled
|
||||
|
||||
Reference in New Issue
Block a user