User & Profile Type Definitions
Source: lib/types/user.ts and lib/types/profile.ts
These modules define types for admin users (authentication and management) and public user profiles (display and portfolio).
User Types (user.ts)
Type Aliases
UserStatus
type UserStatus = 'active' | 'inactive';
Interfaces
AuthUserData
Authentication-only user data stored in the users table. This is the minimal user representation used for auth flows.
interface AuthUserData {
id: string;
email: string;
username?: string;
name?: string;
title?: string;
avatar?: string;
role?: string;
roleName?: string;
status?: UserStatus;
created_by?: string;
created_at: string;
updated_at: string;
}
UserData
Full user data that includes profile information from clientProfiles. This is the complete user representation used throughout the admin dashboard.
interface UserData {
id: string;
username: string;
email: string;
name: string;
title?: string;
avatar?: string;
role: string;
roleName?: string;
status: UserStatus;
created_at: string;
updated_at: string;
created_by: string;
}
Difference from AuthUserData: In UserData, username, name, role, status, and created_by are all required (non-optional).
CreateUserRequest
Payload for creating a new admin user.
interface CreateUserRequest {
username: string;
email: string;
name: string;
title?: string;
avatar?: string;
role: string;
password: string;
}
UpdateUserRequest
Payload for updating an existing user. All fields are optional.
interface UpdateUserRequest {
username?: string;
email?: string;
name?: string;
title?: string;
avatar?: string;
role?: string;
status?: UserStatus;
}
AuthUserListResponse
Paginated response for authentication user lists.
interface AuthUserListResponse {
users: AuthUserData[];
total: number;
page: number;
limit: number;
totalPages: number;
}
UserListResponse
Paginated response for full user lists.
interface UserListResponse {
users: UserData[];
total: number;
page: number;
limit: number;
totalPages: number;
}
UserResponse
Single user response wrapper.
interface UserResponse {
user: UserData;
}
UserListOptions
Query parameters for filtering and paginating user lists.
interface UserListOptions {
includeInactive?: boolean;
sortBy?: 'name' | 'username' | 'email' | 'role' | 'created_at';
sortOrder?: 'asc' | 'desc';
page?: number;
limit?: number;
search?: string;
role?: string;
status?: UserStatus;
}
UserWithCount
Extended user data with an optional count field for statistics.
interface UserWithCount extends UserData {
count?: number;
}
Zod Validation Schemas
userValidationSchema
Full validation schema for creating a user:
const userValidationSchema = z.object({
username: z.string()
.min(3, 'Username must be at least 3 characters')
.max(30, 'Username must be less than 30 characters')
.regex(/^[a-zA-Z0-9_-]+$/,
'Username can only contain letters, numbers, hyphens, and underscores'),
email: z.string()
.email('Invalid email address')
.max(255, 'Email must be less than 255 characters'),
name: z.string()
.min(2, 'Name must be at least 2 characters')
.max(100, 'Name must be less than 100 characters'),
title: z.string()
.max(100, 'Title must be less than 100 characters')
.optional(),
role: z.string()
.min(1, 'Role is required'),
status: z.enum(['active', 'inactive']).optional(),
password: z.string()
.min(8, 'Password must be at least 8 characters')
.regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
'Password must contain at least one lowercase letter, '
+ 'one uppercase letter, and one number'),
});
updateUserValidationSchema
Partial schema for updates (excludes password):
const updateUserValidationSchema = userValidationSchema
.partial()
.omit({ password: true });
Validation Constants
const USER_VALIDATION = {
USERNAME_MIN_LENGTH: 3,
USERNAME_MAX_LENGTH: 30,
NAME_MIN_LENGTH: 2,
NAME_MAX_LENGTH: 100,
TITLE_MAX_LENGTH: 100,
EMAIL_MAX_LENGTH: 255,
PASSWORD_MIN_LENGTH: 8,
AVATAR_MAX_SIZE: 2 * 1024 * 1024, // 2MB
AVATAR_ALLOWED_TYPES: ['image/jpeg', 'image/png', 'image/webp'],
} as const;
Helper Functions
isValidUserStatus
Type guard that narrows a string to UserStatus:
function isValidUserStatus(status: string): status is UserStatus {
return status === 'active' || status === 'inactive';
}
generateUserId
Generates a UUID for a new user:
function generateUserId(): string {
return crypto.randomUUID();
}
formatDateForYaml
Formats a Date as a YAML-compatible string (YYYY-MM-DD HH:mm):
function formatDateForYaml(date: Date = new Date()): string;
// Example: formatDateForYaml(new Date('2025-03-15T10:30:00'))
// => "2025-03-15 10:30"
Profile Types (profile.ts)
Profile
Public-facing user profile structure used for the profile display page.
interface Profile {
username: string;
displayName: string;
bio: string;
avatar: string;
location: string;
company: string;
jobTitle: string;
website: string;
socialLinks: Array<{
platform: string;
url: string;
displayName: string;
}>;
skills: Array<{
name: string;
level: number;
}>;
interests: string[];
portfolio: Array<{
id: string;
title: string;
description: string;
imageUrl: string;
externalUrl: string;
tags: string[];
isFeatured: boolean;
}>;
themeColor: string;
isPublic: boolean;
memberSince: string;
submissions: Array<{
id: string;
title: string;
description: string;
category: string;
status: 'approved' | 'pending' | 'rejected';
submittedAt: string;
updatedAt: string;
url: string;
imageUrl?: string;
}>;
}
Nested types explained:
socialLinks- Links to external social platforms (GitHub, LinkedIn, Twitter, etc.)skills- Skill entries with a proficiencylevel(e.g., 1-5 or 1-100)portfolio- Showcase items with optional featured flag for prominent displaysubmissions- Items submitted by this user, with their current review status
Usage Examples
Creating a user with validation
import { userValidationSchema } from '@/lib/types/user';
import type { CreateUserRequest } from '@/lib/types/user';
const input: CreateUserRequest = {
username: 'johndoe',
email: 'john@example.com',
name: 'John Doe',
role: 'editor',
password: 'SecurePass1',
};
const result = userValidationSchema.safeParse(input);
if (!result.success) {
console.error(result.error.flatten());
}
Filtering users
import type { UserListOptions } from '@/lib/types/user';
const options: UserListOptions = {
role: 'editor',
status: 'active',
sortBy: 'created_at',
sortOrder: 'desc',
page: 1,
limit: 25,
search: 'john',
};
Using the profile type
import type { Profile } from '@/lib/types/profile';
function renderProfile(profile: Profile) {
const visibleSkills = profile.skills
.filter(s => s.level >= 3)
.sort((a, b) => b.level - a.level);
const featuredPortfolio = profile.portfolio
.filter(p => p.isFeatured);
return { visibleSkills, featuredPortfolio };
}
Related Types
RoleDatafromrole.tsdefines the roles referenced inUserData.roleClientProfileWithAuthfromlib/db/queriesis the database-level user profile representationProfileis the public-facing profile, whileUserDatais the admin-facing representation