- Build complete Next.js CRM for commercial real estate - Add authentication with JWT sessions and role-based access - Add GoHighLevel API integration for contacts, conversations, opportunities - Add AI-powered Control Center with tool calling - Add Setup page with onboarding checklist (/setup) - Add sidebar navigation with Setup menu item - Fix type errors in onboarding API, GHL services, and control center tools - Add Prisma schema with SQLite for local development - Add UI components with clay morphism design system Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
92 lines
2.1 KiB
TypeScript
92 lines
2.1 KiB
TypeScript
import { type ClassValue, clsx } from "clsx";
|
|
|
|
/**
|
|
* Utility function to merge class names
|
|
*/
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return clsx(inputs);
|
|
}
|
|
|
|
/**
|
|
* Format currency values
|
|
*/
|
|
export function formatCurrency(
|
|
amount: number,
|
|
currency: string = "USD",
|
|
locale: string = "en-US"
|
|
): string {
|
|
return new Intl.NumberFormat(locale, {
|
|
style: "currency",
|
|
currency,
|
|
}).format(amount);
|
|
}
|
|
|
|
/**
|
|
* Format date values
|
|
*/
|
|
export function formatDate(
|
|
date: Date | string,
|
|
options: Intl.DateTimeFormatOptions = {
|
|
year: "numeric",
|
|
month: "short",
|
|
day: "numeric",
|
|
}
|
|
): string {
|
|
const d = typeof date === "string" ? new Date(date) : date;
|
|
return d.toLocaleDateString("en-US", options);
|
|
}
|
|
|
|
/**
|
|
* Format relative time (e.g., "2 hours ago")
|
|
*/
|
|
export function formatRelativeTime(date: Date | string): string {
|
|
const d = typeof date === "string" ? new Date(date) : date;
|
|
const now = new Date();
|
|
const diffInSeconds = Math.floor((now.getTime() - d.getTime()) / 1000);
|
|
|
|
if (diffInSeconds < 60) return "just now";
|
|
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)} minutes ago`;
|
|
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)} hours ago`;
|
|
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)} days ago`;
|
|
|
|
return formatDate(d);
|
|
}
|
|
|
|
/**
|
|
* Truncate text with ellipsis
|
|
*/
|
|
export function truncate(text: string, length: number = 100): string {
|
|
if (text.length <= length) return text;
|
|
return text.slice(0, length) + "...";
|
|
}
|
|
|
|
/**
|
|
* Generate a random ID
|
|
*/
|
|
export function generateId(length: number = 8): string {
|
|
return Math.random()
|
|
.toString(36)
|
|
.substring(2, 2 + length);
|
|
}
|
|
|
|
/**
|
|
* Debounce function
|
|
*/
|
|
export function debounce<T extends (...args: unknown[]) => unknown>(
|
|
func: T,
|
|
wait: number
|
|
): (...args: Parameters<T>) => void {
|
|
let timeout: NodeJS.Timeout;
|
|
return (...args: Parameters<T>) => {
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(() => func(...args), wait);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Sleep/delay function
|
|
*/
|
|
export function sleep(ms: number): Promise<void> {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|