Appearance
Login Page ​
File Information ​
- Path:
app/pages/[brand_name]/login.vue - Route:
/{brand_name}/login - Purpose: User authentication with email/password and workspace selection
- Layout:
login-layout - Middleware:
check-url,redirect-if-logged-in
Overview ​
The Login page provides secure user authentication using email and password credentials. It automatically detects the workspace from the brand name in the URL and handles the authentication flow with proper error handling and validation.
Key Features ​
Email/Password Authentication
- Email input with validation
- Password input with validation
- Form submission handling
- Loading states during authentication
Workspace Detection
- Automatic workspace detection from brand name
- Brand details fetching if workspace not cached
- Workspace ID resolution for login
Form Validation
- Email format validation
- Required field validation
- Real-time error display
- Submit button state management
Error Handling
- Login error display
- Network error handling
- User-friendly error messages
- Password error highlighting
Auto Redirect
- Redirects authenticated users away from login page
- Redirects to dashboard after successful login
- Preserves brand name in navigation
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 component for navigation
Composables Used ​
useAuthStore()- Authentication store (app/stores/auth.ts)useAppDataStore()- Application data store (app/stores/appData.ts)useSnackbar()- Snackbar notificationsuseHelpers()- Helper functions
Data Properties ​
Reactive Data ​
typescript
{
form: {
email: string, // User email address
password: string // User password
},
contentLoading: boolean, // Initial content loading state
loading: boolean, // Login submission loading state
emailError: string | null, // Email validation error
passwordError: string | null // Password validation error
}Computed Properties ​
brandName ​
Returns brand name from route parameters.
typescript
computed(() => {
return (route.params.brand_name as string) || "Brand Portal"
})disableSubmitBtn ​
Determines if submit button should be disabled.
typescript
computed(() => {
return (
!!emailError.value ||
!!passwordError.value ||
!form.value.email.trim() ||
!form.value.password.trim() ||
loading.value
)
})Returns: boolean - True if form is invalid or loading
Methods ​
validateEmail() ​
Validates email format and required field.
typescript
function validateEmail() {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!form.value.email.trim()) {
emailError.value = "Email address is required"
return
}
if (!emailRegex.test(form.value.email)) {
emailError.value = "Please enter valid email address."
return
}
emailError.value = null
}Validation Rules:
- Email is required
- Must match email regex pattern
- Sets
emailErroron validation failure
validatePassword() ​
Validates password required field.
typescript
function validatePassword() {
if (!form.value.password.trim()) {
passwordError.value = "Password is required"
return
}
passwordError.value = null
}Validation Rules:
- Password is required
- Sets
passwordErroron validation failure
login() ​
Handles login form submission.
typescript
async function login() {
validateEmail()
validatePassword()
if (disableSubmitBtn.value) return
loading.value = true
try {
const authStore = useAuthStore()
const brandName = route.params.brand_name as string
// Get workspace ID
let workspace_id = appDataStore.brand?.workspace?.url_slug || null
if (!workspace_id) {
const response = await appDataStore.fetchBrandDetails(brandName)
workspace_id = response?.workspace?.url_slug || null
}
// Perform login
const result = await authStore.login(
form.value.email,
form.value.password,
workspace_id
)
if (result.success) {
await authStore.getUser()
await navigateTo(`/${brandName}`)
} else {
passwordError.value = result.error || "Login failed"
snackbar.showError(result.error || "Login failed")
}
} catch (error: any) {
const errorMsg =
error.data?.message ||
error.message ||
"An error occurred during login"
passwordError.value = errorMsg
snackbar.showError(errorMsg)
} finally {
loading.value = false
}
}Flow:
- Validates email and password
- Checks if form is valid
- Fetches workspace ID from brand details if needed
- Calls auth store login method
- Fetches user data on success
- Navigates to dashboard
- Handles errors with user feedback
Error Handling:
- Displays error in password field
- Shows snackbar error notification
- Handles network and API errors
User Interactions ​
Form Input
- Enter email address
- Enter password
- Real-time validation on blur
Form Submission
- Click Login button
- Form validates before submission
- Loading state during authentication
Navigation
- Click "Forgot Password" link to navigate to password recovery
- Automatic redirect if already authenticated
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
Watchers ​
None - uses event handlers (@blur, @input) for validation.
Lifecycle Hooks ​
onMounted ​
Initializes content loading state.
typescript
onMounted(() => {
nextTick(() => (contentLoading.value = false))
})Purpose: Hides skeleton loader after component mounts.
API Endpoints ​
Login ​
- Endpoint:
POST /login(viaauthStore.login()) - Request Body:typescript
{ email: string password: string workspace_id: string | number } - Response:typescript
{ success: boolean token?: string user?: User error?: string }
Get Brand Details ​
- Endpoint:
GET /brand/{brand_name}(viaappDataStore.fetchBrandDetails()) - Response: Brand object with workspace information
Get User ​
- Endpoint:
GET /user(viaauthStore.getUser()) - Response: User object with full profile data
User Flow ​
- User navigates to
/{brand_name}/login - Middleware checks if user is already authenticated
- If authenticated, redirects to dashboard
- If not authenticated, displays login form
- User enters email and password
- Form validates on blur/input
- User clicks Login button
- System fetches workspace ID if needed
- Login API call is made
- On success:
- Token stored in HTTP-only cookie
- User data fetched
- Redirect to dashboard
- On failure:
- Error displayed in password field
- Snackbar notification shown
Styling ​
Uses scoped styles from @/assets/scss/login.scss:
- Form layout styles
- Input field styles
- Button styles
- Error message styles
- Skeleton loader styles
Notes for Development ​
- Page requires
check-urlmiddleware to validate brand name - Uses
redirect-if-logged-inmiddleware to prevent authenticated access - Workspace ID is cached in appData store after first fetch
- Email validation uses regex pattern
- Password validation is minimal (required only)
- Error messages are user-friendly
- Loading state prevents multiple submissions
- Form submission uses
@submit.preventto prevent default
Related Documentation ​
- Auth Store - Authentication state management
- App Data Store - Brand and workspace data
- Global Auth Middleware - Global route protection
- Per-Route Auth Middleware - Per-route authentication
- Auth API - Authentication endpoints