import { format } from "date-fns"; import { redirect } from "next/navigation"; import { DateFormatOptions, DateFormatPattern } from "../_lib/types/date-format.interface"; import { toast } from "sonner"; import { IUserSchema } from "@/src/entities/models/users/users.model"; /** * Redirects to a specified path with an encoded message as a query parameter. * @param {('error' | 'success')} type - The type of message, either 'error' or 'success'. * @param {string} path - The path to redirect to. * @param {string} message - The message to be encoded and added as a query parameter. * @returns {never} This function doesn't return as it triggers a redirect. */ export function encodedRedirect( type: "error" | "success", path: string, message: string, ) { return redirect(`${path}?${type}=${encodeURIComponent(message)}`); } /** * Formats a URL by removing any trailing slashes. * @param {string} url - The URL to format. * @returns {string} The formatted URL. */ // Helper function to ensure URLs are properly formatted export function formatUrl(url: string): string { // If URL starts with a slash, it's already absolute if (url.startsWith("/")) { return url; } // Otherwise, ensure it's properly formatted relative to root // Remove any potential duplicated '/dashboard' prefixes if (url.startsWith("dashboard/")) { return "/" + url; } return "/" + url; } /** * Creates a FormData object from the FormData object. * @returns {FormData} The FormData object. */ export function createFormData(): FormData { const data = new FormData(); Object.entries(FormData).forEach(([key, value]) => { if (value) { data.append(key, value); } }); return data; }; /** * Generates a unique username based on the provided email address. * * The username is created by combining the local part of the email (before the '@' symbol) * with a randomly generated alphanumeric suffix. * * @param email - The email address to generate the username from. * @returns A string representing the generated username. * * @example * ```typescript * const username = generateUsername("example@gmail.com"); * console.log(username); // Output: "example.abc123" (random suffix will vary) * ``` */ export function generateUsername(email: string): string { const [localPart] = email.split("@"); const randomSuffix = Math.random().toString(36).substring(2, 8); // Generate a random alphanumeric string return `${localPart}.${randomSuffix}`; } /** * Formats a date string to a human-readable format with type safety. * @param date - The date string to format. * @param options - Formatting options or a format string. * @returns The formatted date string. * @example * // Using default format * formatDate("2025-03-23") * * // Using a custom format string * formatDate("2025-03-23", "yyyy-MM-dd") * * // Using formatting options * formatDate("2025-03-23", { format: "MMMM do, yyyy", fallback: "Not available" }) */ export const formatDate = ( date: string | Date | undefined | null, options: DateFormatOptions | DateFormatPattern = { format: "PPpp" } ): string => { if (!date) { return typeof options === "string" ? "-" : (options.fallback || "-"); } const dateObj = date instanceof Date ? date : new Date(date); // Handle invalid dates if (isNaN(dateObj.getTime())) { return typeof options === "string" ? "-" : (options.fallback || "-"); } if (typeof options === "string") { return format(dateObj, options); } const { format: formatPattern = "PPpp", locale } = options; return locale ? format(dateObj, formatPattern, { locale }) : format(dateObj, formatPattern); }; export const copyItem = (item: string, options?: { label?: string, onSuccess?: () => void, onError?: (error: unknown) => void }) => { if (!navigator.clipboard) { const error = new Error("Clipboard not supported"); toast.error("Clipboard not supported"); options?.onError?.(error); return; } if (!item) { const error = new Error("Nothing to copy"); toast.error("Nothing to copy"); options?.onError?.(error); return; } navigator.clipboard.writeText(item) .then(() => { const label = options?.label || item; toast.success(`${label} copied to clipboard`); options?.onSuccess?.(); }) .catch((error) => { toast.error("Failed to copy to clipboard"); options?.onError?.(error); }); }; /** * Formats a date string to a human-readable format with type safety. * @param date - The date string to format. * @param options - Formatting options or a format string. * @returns The formatted date string. * @example * // Using default format * formatDate("2025-03-23") * * // Using a custom format string * formatDate("2025-03-23", "yyyy-MM-dd") * * // Using formatting options * formatDate("2025-03-23", { format: "MMMM do, yyyy", fallback: "Not available" }) */ export const formatDateWithFallback = ( date: string | Date | undefined | null, options: DateFormatOptions | DateFormatPattern = { format: "PPpp" } ): string => { if (!date) { return typeof options === "string" ? "-" : (options.fallback || "-"); } const dateObj = date instanceof Date ? date : new Date(date); // Handle invalid dates if (isNaN(dateObj.getTime())) { return typeof options === "string" ? "-" : (options.fallback || "-"); } if (typeof options === "string") { return format(dateObj, options); } const { format: formatPattern = "PPpp", locale } = options; return locale ? format(dateObj, formatPattern, { locale }) : format(dateObj, formatPattern); } export const formatDateWithLocale = ( date: string | Date | undefined | null, options: DateFormatOptions | DateFormatPattern = { format: "PPpp" } ): string => { if (!date) { return typeof options === "string" ? "-" : (options.fallback || "-"); } const dateObj = date instanceof Date ? date : new Date(date); // Handle invalid dates if (isNaN(dateObj.getTime())) { return typeof options === "string" ? "-" : (options.fallback || "-"); } if (typeof options === "string") { return format(dateObj, options); } const { format: formatPattern = "PPpp", locale } = options; return locale ? format(dateObj, formatPattern, { locale }) : format(dateObj, formatPattern); }; /** * Formats a date string to a human-readable format with type safety. * @param date - The date string to format. * @param options - Formatting options or a format string. * @returns The formatted date string. * @example * // Using default format * formatDate("2025-03-23") * * // Using a custom format string * formatDate("2025-03-23", "yyyy-MM-dd") * * // Using formatting options * formatDate("2025-03-23", { format: "MMMM do, yyyy", fallback: "Not available" }) */ export const formatDateWithLocaleAndFallback = ( date: string | Date | undefined | null, options: DateFormatOptions | DateFormatPattern = { format: "PPpp" } ): string => { if (!date) { return typeof options === "string" ? "-" : (options.fallback || "-"); } const dateObj = date instanceof Date ? date : new Date(date); // Handle invalid dates if (isNaN(dateObj.getTime())) { return typeof options === "string" ? "-" : (options.fallback || "-"); } if (typeof options === "string") { return format(dateObj, options); } const { format: formatPattern = "PPpp", locale } = options; return locale ? format(dateObj, formatPattern, { locale }) : format(dateObj, formatPattern); } /** * Generates a full name from first and last names. * @param firstName - The first name. * @param lastName - The last name. * @returns The full name or "User" if both names are empty. */ export const getFullName = (firstName: string, lastName: string): string => { return `${firstName} ${lastName}`.trim() || "User"; } /** * Generates initials for a user based on their first and last names. * @param firstName - The first name. * @param lastName - The last name. * @param email - The email address. * @returns The initials or "U" if both names are empty. */ export const getInitials = (firstName: string, lastName: string, email: string): string => { if (firstName && lastName) { return `${firstName[0]}${lastName[0]}`.toUpperCase(); } if (firstName) { return firstName[0].toUpperCase(); } if (email) { return email[0].toUpperCase(); } return "U"; } export function calculateUserStats(users: IUserSchema[] | undefined) { if (!users || !Array.isArray(users)) { return { totalUsers: 0, activeUsers: 0, inactiveUsers: 0, activePercentage: '0.0', inactivePercentage: '0.0', }; } const totalUsers = users.length; const activeUsers = users.filter( (user) => !user.banned_until && user.email_confirmed_at ).length; const inactiveUsers = totalUsers - activeUsers; return { totalUsers, activeUsers, inactiveUsers, activePercentage: totalUsers > 0 ? ((activeUsers / totalUsers) * 100).toFixed(1) : '0.0', inactivePercentage: totalUsers > 0 ? ((inactiveUsers / totalUsers) * 100).toFixed(1) : '0.0', }; }