Add database tables and repository for managing bookings Create API endpoints for booking submission and capacity management Update landing page to persist bookings before WhatsApp redirection
95 lines
2.3 KiB
TypeScript
95 lines
2.3 KiB
TypeScript
export const DEFAULT_USER_PASSWORD = '123456'
|
|
export const MIN_PASSWORD_LENGTH = 8
|
|
export const MIN_FULL_NAME_LENGTH = 2
|
|
export const USERNAME_PATTERN = /^[a-z0-9._-]{3,32}$/
|
|
export const PHONE_NUMBER_PATTERN = /^\+?\d{8,15}$/
|
|
|
|
export type UserRole = 'super_admin' | 'staff'
|
|
|
|
export function normalizeUsername(value: string) {
|
|
return value.trim().toLowerCase()
|
|
}
|
|
|
|
export function normalizeFullName(value: string) {
|
|
return value.trim()
|
|
}
|
|
|
|
export function normalizePhoneNumber(value: string) {
|
|
const trimmed = value.trim()
|
|
|
|
if (!trimmed) {
|
|
return ''
|
|
}
|
|
|
|
const hasPlusPrefix = trimmed.startsWith('+')
|
|
const digitsOnly = trimmed.replace(/\D/g, '')
|
|
|
|
return hasPlusPrefix ? `+${digitsOnly}` : digitsOnly
|
|
}
|
|
|
|
export function isValidPhoneNumber(value: string) {
|
|
return PHONE_NUMBER_PATTERN.test(normalizePhoneNumber(value))
|
|
}
|
|
|
|
export function isValidUsername(value: string) {
|
|
return USERNAME_PATTERN.test(normalizeUsername(value))
|
|
}
|
|
|
|
export function hasValidFullName(value: string) {
|
|
return normalizeFullName(value).length >= MIN_FULL_NAME_LENGTH
|
|
}
|
|
|
|
export function isUserRole(value: string | null | undefined): value is UserRole {
|
|
return value === 'super_admin' || value === 'staff'
|
|
}
|
|
|
|
export interface AuthUser {
|
|
id: string
|
|
username: string
|
|
fullName: string
|
|
phoneNumber: string | null
|
|
role: UserRole
|
|
isActive: boolean
|
|
mustChangePassword: boolean
|
|
needsPasskeySetup: boolean
|
|
passkeyCount: number
|
|
createdAt: string
|
|
lastLoginAt: string | null
|
|
}
|
|
|
|
export interface ManagedUser extends AuthUser {
|
|
createdBy: string | null
|
|
}
|
|
|
|
export interface PublicContact {
|
|
id: string
|
|
fullName: string
|
|
phoneNumber: string
|
|
role: UserRole
|
|
}
|
|
|
|
export interface PasskeySummary {
|
|
id: string
|
|
label: string
|
|
createdAt: string
|
|
lastUsedAt: string | null
|
|
deviceType: 'singleDevice' | 'multiDevice'
|
|
backedUp: boolean
|
|
}
|
|
|
|
export function needsUserOnboarding(
|
|
user: Pick<AuthUser, 'mustChangePassword' | 'needsPasskeySetup'> | null | undefined
|
|
) {
|
|
return Boolean(user && (user.mustChangePassword || user.needsPasskeySetup))
|
|
}
|
|
|
|
export function getDefaultAuthenticatedPath(
|
|
user: Pick<AuthUser, 'role' | 'mustChangePassword' | 'needsPasskeySetup'>
|
|
) {
|
|
if (needsUserOnboarding(user)) {
|
|
return '/security'
|
|
}
|
|
|
|
return user.role === 'super_admin' ? '/management/users' : '/bookings'
|
|
}
|