Skip to content

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 ​

  1. Reset Token Validation

    • Validates reset token from URL query
    • Fetches reset password details from API
    • Handles expired/invalid tokens
    • Error state display
  2. Form Fields

    • Password input with validation
    • Confirm password input with validation
    • Email display (read-only, from reset token)
  3. Form Validation

    • Password required and minimum length (6 characters)
    • Confirm password match validation
    • Real-time error display
    • Submit button state management
  4. Error Handling

    • Invalid/expired token handling
    • Form validation errors
    • API error handling
    • User-friendly error messages
  5. Timezone Support

    • Automatically detects user timezone
    • Sends timezone with password reset request

Components Used ​

  • v-text-field - Vuetify text input components
  • v-btn - Vuetify button component
  • v-progress-circular - Loading spinner
  • v-skeleton-loader - Skeleton loader for initial state

Composables Used ​

  • useAuthApi() - Authentication API (app/composables/api/useAuthApi.ts)
  • useSnackbar() - Snackbar notifications
  • useHelpers() - 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 passwordError on 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 confirmPasswordError on 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:

  1. Validates password and confirm password
  2. Checks if form is valid
  3. Detects user timezone
  4. Calls reset password API with tokens, password, and timezone
  5. Shows success message
  6. Redirects to login page (with user's brand URL from response)
  7. Handles errors with user feedback

User Interactions ​

  1. Form Input

    • Enter new password
    • Enter confirm password
    • Real-time validation on input
    • Error display on validation failure
  2. Form Submission

    • Click "Submit" button
    • Form validates before submission
    • Loading state during API call
  3. 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 (via authApi.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 (via authApi.resetPassword())
  • Request Body:
    typescript
    {
      email_token: string,
      token: string,
      password: string,
      timezone: string
    }
  • Response:
    typescript
    {
      message: string,
      data: {
        user: {
          url: string
        }
      }
    }

User Flow ​

  1. User clicks password reset link with token query parameter
  2. Page validates reset token on server-side
  3. If token is invalid/expired:
    • Error message displayed
    • No form shown
  4. If token is valid:
    • Form displayed with user email (read-only)
    • User enters new password and confirm password
    • Form validates on input
  5. User clicks "Submit" button
  6. System updates user password
  7. On success:
    • Success message displayed
    • Redirect to login page (with user's brand URL from response)
  8. 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 token query parameter
  • Uses useAsyncData for 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