Appearance
Generate Password Page ​
File Information ​
- Path:
app/pages/[brand_name]/generate-password.vue - Route:
/{brand_name}/generate-password - Purpose: Password generation page for new users via invitation link
- Layout:
login-layout - Middleware:
check-url,redirect-if-logged-in
Overview ​
The Generate Password page allows new users to set their password when they receive an invitation link. Users enter their name and password, and the system creates their account with the provided credentials.
Key Features ​
Invitation Token Validation
- Validates invitation token from URL query
- Fetches invitation details from API
- Handles expired/invalid tokens
- Error state display
Form Fields
- Name input (pre-filled from invitation)
- Password input with validation
- Confirm password input with validation
- Email display (read-only, from invitation)
Form Validation
- Name required 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 generation request
Components Used ​
v-text-field- Vuetify text input componentsv-btn- Vuetify button componentv-progress-circular- Loading spinnerv-skeleton-loader- Skeleton loader for initial stateNuxtLink- Nuxt link componentAsyncIcon- Async icon component
Composables Used ​
useAppDataStore()- Application data storeuseSnackbar()- Snackbar notificationsuseHelpers()- Helper functions
Data Properties ​
Reactive Data ​
typescript
{
form: {
name: string, // User full name
email: string, // User email (from invitation)
password: string, // New password
confirm_password: string, // Confirm password
logo: string, // Brand logo
reset_token: string, // Reset token (from invitation)
invitation_token: string, // Invitation token (from URL)
timezone: string // User timezone
},
contentLoading: boolean, // Initial content loading state
submitting: boolean, // Form submission loading state
error: boolean, // Error state flag
message: string, // Error message
nameError: string | null, // Name validation error
passwordError: string | null, // Password validation error
confirmPasswordError: string | null // Confirm password validation error
}Computed Properties ​
brandName ​
Returns brand name from route parameters.
typescript
computed(() =>
(route.params.brand_name as string) || (route.query.brand_name as string)
)disableSubmitBtn ​
Determines if submit button should be disabled.
typescript
computed(() => {
const hasErrors =
!!nameError.value ||
!!passwordError.value ||
!!confirmPasswordError.value
const emptyFields =
!form.value.name.trim() ||
!form.value.password ||
!form.value.confirm_password
return hasErrors || emptyFields || submitting.value
})Returns: boolean - True if form is invalid or loading
Methods ​
validateName() ​
Validates name required field.
typescript
function validateName() {
if (!form.value.name.trim()) {
nameError.value = "Name is required"
return
}
nameError.value = null
}Validation Rules:
- Name is required
- Sets
nameErroron validation failure
validatePassword() ​
Validates password required field and minimum length.
typescript
function validatePassword() {
if (!form.value.password) {
passwordError.value = "Password is required"
return
}
if (form.value.password.length < 6) {
passwordError.value = "Password must be at least 6 characters"
return
}
passwordError.value = null
}Validation Rules:
- Password is required
- Minimum 6 characters
- Sets
passwordErroron validation failure
validateConfirmPassword() ​
Validates confirm password matches password.
typescript
function validateConfirmPassword() {
if (!form.value.confirm_password) {
confirmPasswordError.value = "Confirm Password is required"
return
}
if (form.value.confirm_password !== form.value.password) {
confirmPasswordError.value =
"Password and Confirm Password did not match."
return
}
confirmPasswordError.value = null
}Validation Rules:
- Confirm password is required
- Must match password exactly
- Sets
confirmPasswordErroron validation failure
handleSubmit() ​
Handles password generation form submission.
typescript
async function handleSubmit() {
validateName()
validatePassword()
validateConfirmPassword()
if (disableSubmitBtn.value) return
submitting.value = true
try {
const payload = {
email_token: form.value.reset_token,
token: form.value.invitation_token,
password: form.value.password,
name: form.value.name,
timezone: form.value.timezone,
}
const response = await $api("generate-password", {
method: "POST",
body: payload,
skipAuth: true,
})
const url = response.data?.data?.user?.url
const successUrl = url || brandName.value
snackbar.showSuccess(
response.data?.message ||
response.data?.data?.message ||
response.message ||
"Password generated successfully!"
)
await navigateTo(`/${successUrl}/login`)
} catch (err: any) {
snackbar.showError(err.data?.message || "Something went wrong")
setTimeout(() => {
navigateTo(`/${brandName.value}/login`)
}, 5000)
} finally {
submitting.value = false
}
}Flow:
- Validates all form fields
- Checks if form is valid
- Builds payload with password, name, tokens, and timezone
- Calls generate password API
- Extracts user URL from response
- Shows success message
- Redirects to login page (with user's brand URL if available)
- Handles errors with redirect after 5 seconds
User Interactions ​
Form Input
- Edit name (pre-filled from invitation)
- Enter password
- Enter confirm password
- Real-time validation on input
Form Submission
- Click "Generate Password" button
- Form validates all fields before submission
- Loading state during API call
Navigation
- Click "Back to Login" link to navigate to login page
- Automatic redirect to login after success
- Error redirect to login after 5 seconds
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 and fetches invitation details.
typescript
onMounted(async () => {
nextTick(() => (contentLoading.value = false))
})useAsyncData (Server-Side) ​
Fetches invitation details on server-side.
typescript
const { data: invitationData } = await useAsyncData(
"invitation-data",
async () => {
const invitation_token = route.query.invitation_token as string
if (!invitation_token) throw createError({ statusCode: 404 })
try {
const response = await $api("get-invitation-details", {
method: "POST",
body: { invitation_token },
skipAuth: true,
})
if (!response.status) {
throw createError({
statusCode: 400,
statusMessage: response.message || "Invalid invitation",
})
}
return response
} catch (err: any) {
const status = err.response?.status || 500
if (err.data?.message?.includes("link is expired")) {
error.value = true
message.value = err.data.message
} else {
throw createError({
statusCode: status,
statusMessage: err.data?.message || err.message,
})
}
}
}
)Purpose: Validates invitation token and pre-fills form with user data.
API Endpoints ​
Get Invitation Details ​
- Endpoint:
POST /get-invitation-details(via$api()) - Request Body:typescript
{ invitation_token: string } - Response:typescript
{ status: boolean, data: { name: string, email: string, reset_token: string, invitation_token: string }, message?: string }
Generate Password ​
- Endpoint:
POST /generate-password(via$api()) - Request Body:typescript
{ email_token: string, token: string, password: string, name: string, timezone: string } - Response:typescript
{ data: { data: { message: string, user: { url: string } }, message: string } }
User Flow ​
- User clicks invitation link with
invitation_tokenquery parameter - Page validates invitation token on server-side
- If token is invalid/expired:
- Error message displayed
- Link to login page shown
- If token is valid:
- Form displayed with pre-filled name and email
- User enters password and confirm password
- Form validates on input
- User clicks "Generate Password" button
- System creates account with provided credentials
- On success:
- Success message displayed
- Redirect to login page (with user's brand URL if available)
- On failure:
- Error message displayed
- Redirect to login after 5 seconds
Security Considerations ​
- Invitation 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
Notes for Development ​
- Page requires
invitation_tokenquery parameter - Uses
useAsyncDatafor server-side token validation - Brand logo fetched from appData store
- Timezone automatically detected using dayjs
- Form pre-filled with invitation data
- Error state shows different UI (no form, just error message)
- Success redirect uses user's brand URL if available
- Error redirect happens after 5 seconds delay
Related Documentation ​
- Login Page - Login page documentation
- Reset Password Page - Password reset page
- Forgot Password Page - Password recovery page
- Global Auth Middleware - Global route protection
- Per-Route Auth Middleware - Per-route authentication