Skip to Content

Utils Documentation

Overview

The utils directory (src/utils/) contains pure utility functions that provide common functionality across the application. These are stateless, reusable functions for data transformation, validation, formatting, and other shared operations.

Directory Structure

src/utils/ ├── checkers.ts # Validation checkers ├── cn.ts # Tailwind class merging ├── create-css-vars.ts # CSS variable generation ├── create-number-array.ts # Number array utilities ├── empty-array-checker.ts # Array validation ├── error-handler.ts # Error handling utilities ├── excel-format-date.ts # Date formatting for Excel ├── first-item-value.ts # Array accessors ├── format-data.ts # Data formatting ├── format-name.ts # Name formatting ├── format-number.ts # Number formatting ├── get-navigation-type.ts # Navigation detection ├── get-server-session.ts # Server session helper ├── get-session-id.ts # Session ID extraction ├── get-user-type-from-session.ts # User type detection ├── mapped-data.ts # Data mapping utilities ├── parse-number-from-string.ts # Number parsing ├── pick-random-item.ts # Random selection ├── provider/ # Provider utilities (1 item) ├── refiner.ts # Data refinement ├── row-selector.ts # Table row selection ├── signout-falsy.ts # Signout helpers ├── success-message.ts # Success messaging ├── to-api-date.ts # Date API formatting ├── type-utils.ts # TypeScript utilities └── with-idx.ts # Index utilities

Core Utilities

Class Name Merging (cn.ts)

Tailwind CSS class merging utility using tailwind-merge and clsx.

import { type ClassValue, clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }

Usage:

import { cn } from '@/utils/cn'; // Conditional classes <div className={cn('base-class', isActive && 'active-class')}> // Override classes <Button className={cn('btn-base', className)}> // Merge conflicting Tailwind classes <div className={cn('px-4 py-2', 'p-6')}> // Results in 'p-6'

Error Handling (error-handler.ts)

Centralized error handling utilities.

export function handleApiError(error: unknown): ApiError; export function handleFormError(error: unknown): FormError; export function getErrorMessage(error: unknown): string; export function isNotFoundError(error: unknown): boolean; export function isForbiddenError(error: unknown): boolean;

Usage:

import { handleApiError, getErrorMessage } from '@/utils/error-handler'; try { await apiCall(); } catch (error) { const apiError = handleApiError(error); toast.error(getErrorMessage(error)); }

Formatting Utilities

Name Formatting (format-name.ts)

export function formatFullName(firstName: string, lastName: string): string; export function formatInitials(firstName: string, lastName: string): string; export function formatLastNameFirst(firstName: string, lastName: string): string;

Usage:

import { formatFullName, formatInitials } from '@/utils/format-name'; formatFullName('John', 'Doe'); // 'John Doe' formatInitials('John', 'Doe'); // 'JD' formatLastNameFirst('John', 'Doe'); // 'Doe, John'

Number Formatting (format-number.ts)

export function formatCurrency(amount: number, currency?: string): string; export function formatPercentage(value: number, decimals?: number): string; export function formatCompactNumber(value: number): string;

Usage:

import { formatCurrency, formatPercentage } from '@/utils/format-number'; formatCurrency(1234.56); // '$1,234.56' formatPercentage(0.1234, 1); // '12.3%' formatCompactNumber(1234567); // '1.2M'

Date Formatting for Excel (excel-format-date.ts)

export function formatDateForExcel(date: Date): string; export function parseExcelDate(value: string | number): Date;

Data Utilities

Data Mapping (mapped-data.ts)

Transform and map data structures:

export function mapArrayToObject<T>( array: T[], keyExtractor: (item: T) => string ): Record<string, T>; export function groupBy<T>( array: T[], keyExtractor: (item: T) => string ): Record<string, T[]>;

Usage:

import { mapArrayToObject, groupBy } from '@/utils/mapped-data'; const membersById = mapArrayToObject(members, m => m.id); // { '1': { id: '1', name: 'John' }, ... } const membersByProvider = groupBy(members, m => m.insuranceProvider); // { 'Cocolife': [...], 'Maxicare': [...] }

Data Refinement (refiner.ts)

Clean and refine data:

export function refineString(value: unknown): string | null; export function refineNumber(value: unknown): number | null; export function refineDate(value: unknown): Date | null; export function refineBoolean(value: unknown): boolean | null; export function removeNullValues<T extends Record<string, unknown>>(obj: T): Partial<T>;

Data Formatting (format-data.ts)

Format various data types for display:

export function formatPhoneNumber(phone: string): string; export function formatAddress(address: Address): string; export function formatMemberId(id: string, provider: string): string;

Array Utilities

Empty Array Checker (empty-array-checker.ts)

export function isEmptyArray<T>(value: T[] | null | undefined): value is []; export function hasItems<T>(value: T[] | null | undefined): value is [T, ...T[]];

Usage:

import { isEmptyArray, hasItems } from '@/utils/empty-array-checker'; if (isEmptyArray(members)) { return <EmptyState />; } if (hasItems(members)) { return <MemberList members={members} />; // TypeScript knows array is non-empty }

Number Array Creator (create-number-array.ts)

export function createNumberArray(length: number, startAt?: number): number[];

Usage:

import { createNumberArray } from '@/utils/create-number-array'; createNumberArray(5); // [0, 1, 2, 3, 4] createNumberArray(5, 1); // [1, 2, 3, 4, 5]

First Item Value (first-item-value.ts)

export function getFirstItem<T>(array: T[]): T | undefined; export function getFirstItemOrDefault<T>(array: T[], defaultValue: T): T;

Random Item Picker (pick-random-item.ts)

export function pickRandomItem<T>(array: T[]): T; export function pickRandomItems<T>(array: T[], count: number): T[];

TypeScript Utilities (type-utils.ts)

Type-level utilities for TypeScript:

export type Nullable<T> = T | null; export type Optional<T> = T | undefined; export type NonEmptyArray<T> = [T, ...T[]]; export type ArrayElement<A> = A extends readonly (infer T)[] ? T : never; export type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]; };

Session Utilities

Server Session (get-server-session.ts)

Next.js server-side session retrieval:

import { type GetServerSidePropsContext } from 'next'; export async function getServerSession(ctx: GetServerSidePropsContext) { // Returns session or null }

Usage in getServerSideProps:

import { getServerSession } from '@/utils/get-server-session'; export const getServerSideProps = async (ctx) => { const session = await getServerSession(ctx); if (!session) { return { redirect: { destination: '/' } }; } return { props: { user: session.user } }; };

Session ID (get-session-id.ts)

Extract session identifier:

export function getSessionId(): string | null; export function generateSessionId(): string;

User Type from Session (get-user-type-from-session.ts)

export function getUserTypeFromSession(session: Session): UserType; export function isBroker(session: Session): boolean; export function isPrincipal(session: Session): boolean; export function isBeneficiary(session: Session): boolean;

Detect navigation method:

export function getNavigationType(): 'reload' | 'navigate' | 'back_forward'; export function isPageReload(): boolean; export function isBackNavigation(): boolean;

Date Utilities

API Date Formatting (to-api-date.ts)

Convert dates to API format:

export function toApiDate(date: Date | string): string; // ISO format export function fromApiDate(dateString: string): Date; export function isValidApiDate(value: string): boolean;

Usage:

import { toApiDate, fromApiDate } from '@/utils/to-api-date'; const apiDate = toApiDate(new Date()); // '2024-01-15T10:30:00.000Z' const date = fromApiDate('2024-01-15'); // Date object

Table Utilities

Row Selector (row-selector.ts)

Table row selection utilities:

export function toggleRowSelection<T>( selectedRows: T[], row: T, identifier: (item: T) => string ): T[]; export function selectAllRows<T>( rows: T[], selectedRows: T[], identifier: (item: T) => string ): T[]; export function isRowSelected<T>( selectedRows: T[], row: T, identifier: (item: T) => string ): boolean;

Usage:

import { toggleRowSelection, isRowSelected } from '@/utils/row-selector'; const [selected, setSelected] = useState<Member[]>([]); const handleSelect = (member: Member) => { setSelected(prev => toggleRowSelection(prev, member, m => m.id)); }; const isSelected = isRowSelected(selected, member, m => m.id);

CSS Variable Utilities

CSS Variables Creator (create-css-vars.ts)

Generate CSS custom properties:

export function createCSSVars(variables: Record<string, string>): string; export function createHSLVars(colors: Record<string, [number, number, number]>): string;

Usage:

import { createCSSVars, createHSLVars } from '@/utils/create-css-vars'; const style = createCSSVars({ '--primary-color': '#007bff', '--spacing-unit': '8px', }); // Returns: '--primary-color: #007bff; --spacing-unit: 8px;' const hslStyle = createHSLVars({ primary: [220, 90, 56], }); // Returns: '--primary: 220 90% 56%;'

Checkers (checkers.ts)

Validation utilities:

export function isValidEmail(email: string): boolean; export function isValidPhone(phone: string): boolean; export function isValidMemberId(id: string): boolean; export function isValidDate(date: unknown): date is Date; export function isObject(value: unknown): value is Record<string, unknown>;

Index Utilities (with-idx.ts)

Add indices to data:

export function withIdx<T>(array: T[]): Array<T & { idx: number }>; export function withUniqueId<T>(array: T[]): Array<T & { uniqueId: string }>;

Usage:

import { withIdx } from '@/utils/with-idx'; const membersWithIndex = withIdx(members); // [{ ...member, idx: 0 }, { ...member, idx: 1 }, ...]

Signout Utilities (signout-falsy.ts)

Handle signout edge cases:

export function shouldSignOut(error: unknown): boolean; export function handleSignoutError(error: unknown): void;

Success Messaging (success-message.ts)

Generate success messages:

export function getSuccessMessage(action: string, entity?: string): string; export function getBatchSuccessMessage(count: number, action: string): string;

Usage:

import { getSuccessMessage, getBatchSuccessMessage } from '@/utils/success-message'; getSuccessMessage('create', 'member'); // 'Member created successfully' getBatchSuccessMessage(5, 'delete'); // '5 items deleted successfully'

Provider Utilities (provider/)

Provider-specific utility functions.

provider/ └── (utility files for insurance providers)

Creating New Utilities

Pattern for New Utilities

// utils/my-utility.ts /** * @description Brief description of what the utility does * @param param1 - Description of first parameter * @param param2 - Description of second parameter * @returns Description of return value * * @example * myUtility('input', { option: true }); * // Returns: 'processed output' */ export interface MyUtilityOptions { option?: boolean; fallback?: string; } export function myUtility( input: string, options: MyUtilityOptions = {} ): string { const { option = false, fallback = '' } = options; if (!input) return fallback; // Implementation return input.toUpperCase(); }

Testing Utilities

// utils/__tests__/my-utility.test.ts import { describe, it, expect } from 'vitest'; import { myUtility } from '../my-utility'; describe('myUtility', () => { it('should handle valid input', () => { expect(myUtility('hello')).toBe('HELLO'); }); it('should return fallback for empty input', () => { expect(myUtility('', { fallback: 'default' })).toBe('default'); }); });

Best Practices

1. Pure Functions

Utilities should be pure - same input always produces same output:

// Good export function double(x: number): number { return x * 2; } // Bad - has side effect let multiplier = 2; export function double(x: number): number { return x * multiplier; // Result depends on external state }

2. Input Validation

Always validate inputs and handle edge cases:

export function safeDivide(a: number, b: number): number | null { if (b === 0) return null; return a / b; }

3. Type Safety

Use TypeScript to ensure type safety:

// Use type guards export function isString(value: unknown): value is string { return typeof value === 'string'; } // Use generics for flexibility export function first<T>(array: T[]): T | undefined { return array[0]; }

4. Documentation

Document all utilities with JSDoc:

/** * Formats a member's full name with title * @param member - The member object * @param includeTitle - Whether to include title prefix * @returns Formatted name string */ export function formatMemberName( member: Member, includeTitle = false ): string { // Implementation }

5. No Business Logic

Keep utilities generic - no business-specific logic:

// Good - generic formatting export function formatCurrency(amount: number): string; // Bad - business logic in utility export function getMemberPrice(member: Member): number; // Move to services

Import Patterns

Barrel Export (Not Currently Used)

Consider adding utils/index.ts for cleaner imports:

// utils/index.ts export { cn } from './cn'; export { formatFullName } from './format-name'; export { formatCurrency } from './format-number'; // ... more exports // Usage import { cn, formatFullName } from '@/utils';

Current Pattern

Import utilities directly from their files:

import { cn } from '@/utils/cn'; import { formatFullName } from '@/utils/format-name'; import { isEmptyArray } from '@/utils/empty-array-checker';

Last updated on