Add packages/common for shared utilities

Create new @reviq/common package with environment-agnostic utilities:
- Date formatting: formatDate, formatDateTime, formatLongDate,
  formatRelativeDate, formatRelativeTime
- User utilities: getUserInitials, formatRole

Consolidate date formatting from publisher-dashboard into shared package.
All utilities include comprehensive test coverage with bun:test.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
igm
2026-01-11 12:34:10 +08:00
parent 7358129802
commit b1d07626f3
25 changed files with 639 additions and 300 deletions

View File

@@ -0,0 +1,51 @@
/**
* User-related utility functions
*/
interface UserLike {
displayName?: string | null;
email: string;
}
/**
* Generate initials from a user's display name or email.
* - For display names with 2+ words: first and last initials (e.g., "John Doe" -> "JD")
* - For single word names: first 2 characters (e.g., "John" -> "JO")
* - Falls back to first 2 characters of email if no display name
* - Returns "??" if user is null/undefined
*
* @example
* getUserInitials({ displayName: "John Doe", email: "john@example.com" }) // "JD"
* getUserInitials({ displayName: "John", email: "john@example.com" }) // "JO"
* getUserInitials({ email: "john@example.com" }) // "JO"
* getUserInitials(null) // "??"
*/
export function getUserInitials(user: UserLike | null | undefined): string {
if (!user) {
return "??";
}
if (user.displayName) {
const parts = user.displayName.split(" ");
const firstPart = parts[0];
const lastPart = parts[parts.length - 1];
if (parts.length >= 2 && firstPart && lastPart) {
return (firstPart.charAt(0) + lastPart.charAt(0)).toUpperCase();
}
return user.displayName.slice(0, 2).toUpperCase();
}
return user.email.slice(0, 2).toUpperCase();
}
/**
* Format a role string for display (capitalizes first letter).
*
* @example
* formatRole("admin") // "Admin"
* formatRole("member") // "Member"
* formatRole("owner") // "Owner"
*/
export function formatRole(role: string): string {
return role.charAt(0).toUpperCase() + role.slice(1);
}