web-production-saas-starter/next_b2b_starter/lib/models/member.model.ts
2025-12-16 18:54:41 +04:00

210 lines
4.8 KiB
TypeScript

// lib/models/member.model.ts
/**
* Member and User Management Models
*/
export type MemberRole = "admin" | "manager" | "member";
export type MemberStatus = "active" | "pending" | "inactive";
/**
* Organization Member
*/
export interface OrganizationMember {
id: string;
email: string;
name?: string;
role: MemberRole;
status: MemberStatus;
avatarUrl?: string;
joinedAt: Date;
invitedAt?: Date;
invitedBy?: string;
}
/**
* Current User Profile
*/
export interface UserProfile {
id: string;
email: string;
name?: string;
avatarUrl?: string;
role: MemberRole;
organizationId: string;
organizationName: string;
}
/**
* Member Invitation Request
*/
export interface InviteMemberRequest {
email: string;
name: string;
role: MemberRole;
sendEmail?: boolean;
}
/**
* Member Invitation Response
*/
export interface InviteMemberResponse {
success: boolean;
memberId?: string;
message?: string;
inviteLink?: string;
}
/**
* Update Profile Request
*/
export interface UpdateProfileRequest {
name?: string;
avatarUrl?: string;
}
/**
* Member List Response
*/
export interface MemberListResponse {
members: OrganizationMember[];
totalCount: number;
hasMore: boolean;
}
/**
* Helper functions for member management
*/
export const MemberHelpers = {
/**
* Get role display configuration
*/
getRoleConfig: (role: MemberRole) => {
const configs = {
admin: {
label: "Admin",
color: "bg-blue-100 text-blue-700 border-blue-200",
description: "Full system control and member management",
},
manager: {
label: "Manager",
color: "bg-emerald-100 text-emerald-700 border-emerald-200",
description: "Elevated access - edit, delete, and approve resources",
},
member: {
label: "Member",
color: "bg-gray-100 text-gray-700 border-gray-200",
description: "Basic access - view and create resources",
},
};
return configs[role] || configs.member;
},
/**
* Get status configuration
*/
getStatusConfig: (status: MemberStatus) => {
const configs = {
active: {
label: "Active",
color: "bg-emerald-100 text-emerald-700",
dotColor: "bg-emerald-500",
},
pending: {
label: "Pending",
color: "bg-amber-100 text-amber-700",
dotColor: "bg-amber-500",
},
inactive: {
label: "Inactive",
color: "bg-gray-100 text-gray-500",
dotColor: "bg-gray-400",
},
};
return configs[status] || configs.inactive;
},
/**
* Get initials from name or email
*/
getInitials: (name?: string, email?: string): string => {
const source = name || email || "?";
const parts = source.trim().split(/\s+/);
if (parts.length > 1) {
// Multiple words - use first letter of first and last
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
} else if (email && !name) {
// Email only - use first two letters
return email.substring(0, 2).toUpperCase();
} else {
// Single word - use first two letters
return source.substring(0, 2).toUpperCase();
}
},
/**
* Generate avatar background color from string
*/
getAvatarColor: (str: string): string => {
const colors = [
"bg-blue-500",
"bg-purple-500",
"bg-pink-500",
"bg-emerald-500",
"bg-amber-500",
"bg-cyan-500",
"bg-rose-500",
"bg-indigo-500",
];
// Handle undefined or empty string
if (!str || str.length === 0) {
return colors[0];
}
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return colors[Math.abs(hash) % colors.length];
},
/**
* Check if user can manage members based on role
*/
canManageMembers: (role: MemberRole): boolean => {
return role === "admin";
},
/**
* Format member joined date
*/
formatJoinedDate: (date: Date | string): string => {
const d = typeof date === "string" ? new Date(date) : date;
const now = new Date();
const diffTime = Math.abs(now.getTime() - d.getTime());
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 0) {
return "Today";
} else if (diffDays === 1) {
return "Yesterday";
} else if (diffDays < 7) {
return `${diffDays} days ago`;
} else if (diffDays < 30) {
const weeks = Math.floor(diffDays / 7);
return `${weeks} ${weeks === 1 ? "week" : "weeks"} ago`;
} else if (diffDays < 365) {
const months = Math.floor(diffDays / 30);
return `${months} ${months === 1 ? "month" : "months"} ago`;
} else {
return d.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
}
},
};