Appearance
Reset Password Page ​
File Information ​
- Path:
app/pages/reset-password.vue - Route:
/reset-password - Purpose: Password reset page for users who received a password reset link
- Layout:
login-layout - Middleware:
redirect-if-logged-in
Overview ​
The Reset Password page allows users to set a new password after clicking a password reset link sent to their email. Users enter a new password and confirm it, and the system updates their account password.
Key Features ​
Reset Token Validation
- Validates reset token from URL query
- Fetches reset password details from API
- Handles expired/invalid tokens
- Error state display
Form Fields
- Password input with validation
- Confirm password input with validation
- Email display (read-only, from reset token)
Form Validation
- Password required and minimum length (6 characters)
- Confirm password match validation
- Real-time error display
- Submit button state management
Error Handling
- Invalid/expired token handling
- Form validation errors
- API error handling
- User-friendly error messages
Timezone Support
- Automatically detects user timezone
- Sends timezone with password reset request
Components Used ​
v-text-field- Vuetify text input componentsv-btn- Vuetify button componentv-progress-circular- Loading spinnerv-skeleton-loader- Skeleton loader for initial state
Composables Used ​
useAuthApi()- Authentication API (app/composables/api/useAuthApi.ts)useSnackbar()- Snackbar notificationsuseHelpers()- Helper functions
Data Properties ​
Reactive Data ​
typescript
{
form: {
email: string, // User email (from reset token)
password: string, // New password
confirm_password: string, // Confirm password
token: string, // Reset token (from URL query)
email_token: string // Email token (from API response)
},
contentLoading: boolean, // Initial content loading state
loading: boolean, // Form submission loading state
error: boolean, // Error state flag
message: string, // Error message
passwordError: string | null, // Password validation error
confirmPasswordError: string | null // Confirm password validation error
}Computed Properties ​
disableSubmitBtn ​
Determines if submit button should be disabled.
typescript
computed(() => {
return (
loading.value ||
!!passwordError.value ||
!!confirmPasswordError.value ||
!form.value.password.trim() ||
!form.value.confirm_password.trim()
)
})Returns: boolean - True if form is invalid or loading
Methods ​
validatePassword() ​
Validates password required field and minimum length.
typescript
function validatePassword() {
if (!form.value.password.trim()) {
passwordError.value = "Password is required"
return false
}
if (form.value.password.length < 6) {
passwordError.value = "Password must be at least 6 characters"
return false
}
passwordError.value = null
return true
}Validation Rules:
- Password is required
- Minimum 6 characters
- Sets
passwordErroron validation failure - Returns boolean for validation result
validateConfirmPassword() ​
Validates confirm password matches password.
typescript
function validateConfirmPassword() {
if (!form.value.confirm_password.trim()) {
confirmPasswordError.value = "Confirm Password is required"
return false
}
if (form.value.confirm_password !== form.value.password) {
confirmPasswordError.value =
"Password and Confirm Password did not match."
return false
}
confirmPasswordError.value = null
return true
}Validation Rules:
- Confirm password is required
- Must match password exactly
- Sets
confirmPasswordErroron validation failure - Returns boolean for validation result
handleSubmit() ​
Handles password reset form submission.
typescript
async function handleSubmit() {
const passwordValid = validatePassword()
const confirmValid = validateConfirmPassword()
if (!passwordValid || !confirmValid || disableSubmitBtn.value) return
loading.value = true
const timezone = dayjs.tz.guess()
try {
const response = await authApi.resetPassword(
form.value.email_token as string,
form.value.token as string,
form.value.password,
timezone
)
snackbar.showSuccess(response.message)
await navigateTo(`/${response.data.user.url}/login`)
} catch (err: any) {
const errMessage =
err.data?.message || err.message || "Something went wrong"
snackbar.showError(errMessage)
} finally {
loading.value = false
}
}Flow:
- Validates password and confirm password
- Checks if form is valid
- Detects user timezone
- Calls reset password API with tokens, password, and timezone
- Shows success message
- Redirects to login page (with user's brand URL from response)
- Handles errors with user feedback
User Interactions ​
Form Input
- Enter new password
- Enter confirm password
- Real-time validation on input
- Error display on validation failure
Form Submission
- Click "Submit" button
- Form validates before submission
- Loading state during API call
Error State
- Invalid/expired token shows error message
- No form displayed in error state
Styling ​
- Uses scoped styles from
@/assets/scss/login.scss - Form layout styles
- Input field styles with validation states
- Button styles with loading states
- Error message styles
- Skeleton loader styles
Lifecycle Hooks ​
onMounted ​
Initializes content loading state.
typescript
onMounted(async () => {
nextTick(() => (contentLoading.value = false))
})useAsyncData (Server-Side) ​
Fetches reset password details on server-side.
typescript
const { data } = await useAsyncData("reset-password-details", async () => {
const query = route.query
const token = query.token as string
if (!token) {
throw createError({
statusCode: 404,
statusMessage: "Token not provided"
})
}
try {
const response = await authApi.getResetPasswordDetails(token)
form.value = {
...form.value,
...response.data,
password: "",
confirm_password: "",
token: token,
email_token: response.data?.email_token || "",
}
error.value = false
return response
} catch (err: any) {
error.value = true
if (err.data?.message?.includes("expired")) {
message.value = err.data.message
return null
} else {
throw createError({
statusCode: err.status || 500,
statusMessage: err.data?.message || err.message,
})
}
}
})Purpose: Validates reset token and pre-fills form with user email.
API Endpoints ​
Get Reset Password Details ​
- Endpoint:
GET /reset-password-details(viaauthApi.getResetPasswordDetails()) - Parameters:typescript
{ token: string // Reset token from URL query } - Response:typescript
{ data: { email: string, email_token: string, token: string } }
Reset Password ​
- Endpoint:
POST /reset-password(viaauthApi.resetPassword()) - Request Body:typescript
{ email_token: string, token: string, password: string, timezone: string } - Response:typescript
{ message: string, data: { user: { url: string } } }
User Flow ​
- User clicks password reset link with
tokenquery parameter - Page validates reset token on server-side
- If token is invalid/expired:
- Error message displayed
- No form shown
- If token is valid:
- Form displayed with user email (read-only)
- User enters new password and confirm password
- Form validates on input
- User clicks "Submit" button
- System updates user password
- On success:
- Success message displayed
- Redirect to login page (with user's brand URL from response)
- On failure:
- Error message displayed
- User can retry
Security Considerations ​
- Reset token required in URL query
- Token validated on server-side
- Expired tokens show error message
- Password minimum length enforced (6 characters)
- Password confirmation required
- Timezone automatically detected and sent
- Token used only once (invalidated after use)
Notes for Development ​
- Page requires
tokenquery parameter - Uses
useAsyncDatafor server-side token validation - Route is
/reset-password(not brand-specific) - Error state shows different UI (no form, just error message)
- Success redirect uses user's brand URL from API response
- Form validation returns boolean for conditional logic
- Timezone automatically detected using dayjs
Related Documentation ​
- Login Page - Login page documentation
- Forgot Password Page - Password recovery page
- Generate Password Page - Password generation page
- Global Auth Middleware - Global route protection
- Per-Route Auth Middleware - Per-route authentication