351 lines
9.6 KiB
TypeScript
351 lines
9.6 KiB
TypeScript
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',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Generate route with query parameters
|
|
* @param baseRoute - The base route path
|
|
* @param params - Object containing query parameters
|
|
* @returns Formatted route with query parameters
|
|
*/
|
|
export const createRoute = (baseRoute: string, params?: Record<string, string>): string => {
|
|
if (!params || Object.keys(params).length === 0) {
|
|
return baseRoute;
|
|
}
|
|
|
|
const queryString = Object.entries(params)
|
|
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
|
.join('&');
|
|
|
|
return `${baseRoute}?${queryString}`;
|
|
};
|