field-enums.ts7.0 KBView on GitHub /**
* CRM Field Enum Utilities
*
* Centralized utilities for working with enum field options (status, priority).
* Provides simplified helpers to eliminate code duplication across components.
*
* This is the single source of truth for:
* - Which columns are enum columns (EnumFieldKey)
* - Which columns support null option in dropdowns
* - Which columns support the unified filter/sort popover
* - Which columns get their options merged from AOPs
*/
import {
DEFAULT_STATUS_OPTIONS,
DEFAULT_PRIORITY_OPTIONS,
} from './types';
import { getColorClass, getTextColorClass, getTypeColor } from './utils';
import type { CrmFieldEnumOption } from './types';
/**
* All column IDs that are enum-based columns.
* These columns have a fixed set of options that can be filtered/sorted.
*/
export type EnumFieldKey=[redacted] | 'priority' | 'type' | 'crmSynced' | 'history' | 'hasFutureCalendar';
/**
* Array of all enum field keys for iteration.
*/
export const ENUM_FIELD_KEYS: EnumFieldKey[] = [
'status',
'priority',
'type',
'crmSynced',
'history',
'hasFutureCalendar',
];
/**
* Type guard to check if a column ID is an enum field.
* Use this to determine if a column supports the unified filter/sort popover.
*/
export function isEnumField(columnId: string): columnId is EnumFieldKey {
return ENUM_FIELD_KEYS.includes(columnId as EnumFieldKey);
}
/**
* Columns that support a null/"-" option in their dropdown.
* These are columns where "no value" is a valid filterable state.
* Note: 'crmSynced' is excluded since it's always computed (never null).
*/
export const COLUMNS_WITH_NULL_OPTION: ReadonlySet<string> = new Set([
'type',
'status',
'priority',
]);
/**
* Check if a column supports null option.
*/
export function supportsNullOption(columnId: string): boolean {
return COLUMNS_WITH_NULL_OPTION.has(columnId);
}
/**
* Fields that get their enum options merged from AOP's conversationFieldDefinitions.
* These fields have values that vary per AOP and need to be merged when multiple AOPs are selected.
*/
export const AOP_MERGEABLE_FIELDS = ['status', 'priority'] as const;
export type AopMergeableField = (typeof AOP_MERGEABLE_FIELDS)[number];
/**
* Check if a field gets its options merged from AOPs.
*/
export function isAopMergeableField(fieldId: string): fieldId is AopMergeableField {
return AOP_MERGEABLE_FIELDS.includes(fieldId as AopMergeableField);
}
/**
* Default enum options for crmSynced column.
* Single source of truth - used by both field-enums and conversation-columns.
*/
export const DEFAULT_CRM_SYNCED_OPTIONS: CrmFieldEnumOption[] = [
{ value: 'synced', label: 'Synced', enumOrder: 0, color: 'green' },
{ value: 'not_synced', label: 'Not Synced', enumOrder: 1, color: 'gray' },
];
/**
* Default enum options for history/timeline column.
* Single source of truth - used by both field-enums and conversation-columns.
*/
export const DEFAULT_HISTORY_OPTIONS: CrmFieldEnumOption[] = [
{ value: 'meeting', label: 'Meeting', enumOrder: 0, color: 'purple' },
{ value: 'email_outbound', label: 'Outbound Email', enumOrder: 1, color: 'pink' },
{ value: 'email_inbound', label: 'Inbound Email', enumOrder: 2, color: 'green' },
];
/**
* Gets the default options array for a given field key
*/
function getDefaultOptions(fieldKey=[redacted] CrmFieldEnumOption[] {
switch (fieldKey) {
case 'status':
return DEFAULT_STATUS_OPTIONS;
case 'priority':
return DEFAULT_PRIORITY_OPTIONS;
case 'type':
return []; // Type field doesn't have default options
case 'crmSynced':
return DEFAULT_CRM_SYNCED_OPTIONS;
case 'history':
return DEFAULT_HISTORY_OPTIONS;
default:
return [];
}
}
/**
* Core function that finds an enum option by value.
* Checks enumOptions first (from AOP definitions), then falls back to default options.
*
* @param value - The enum value to find
* @param fieldKey - The field key ('status', 'priority', 'type')
* @param enumOptions - Optional array of enum options from AOP definitions
* @returns The found option object or null
*/
export function getEnumOption(
value: string | null | undefined,
fieldKey=[redacted],
enumOptions?: CrmFieldEnumOption[],
): CrmFieldEnumOption | null {
if (!value) return null;
const normalizedValue = value?.toLowerCase();
// First, check enumOptions (from AOP definitions)
if (enumOptions && enumOptions.length > 0) {
const option = enumOptions.find((opt) => opt.value?.toLowerCase() === normalizedValue);
if (option) return option;
}
// Fallback to default options
const defaultOptions = getDefaultOptions(fieldKey);
const defaultOption = defaultOptions.find((opt) => opt.value?.toLowerCase() === normalizedValue);
return defaultOption || null;
}
/**
* Gets the Tailwind color class for an enum value.
* Handles special case for 'type' field which uses getTypeColor.
*
* @param value - The enum value
* @param fieldKey - The field key ('status', 'priority', 'type')
* @param enumOptions - Optional array of enum options from AOP definitions
* @returns Tailwind color class string
*/
export function getEnumColor(
value: string | null | undefined,
fieldKey=[redacted],
enumOptions?: CrmFieldEnumOption[],
): string {
// Handle null values explicitly - always gray
if (value === null) {
return 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400';
}
// Special handling for 'type' field
if (fieldKey === 'type') {
return getTypeColor(value);
}
// Special handling for 'crmSynced' field
if (fieldKey === 'crmSynced') {
if (value === 'synced') {
return 'bg-green-100 text-green-800 dark:bg-green-900/50 dark:text-green-300';
}
return 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400';
}
// Special handling for 'history' field (event types)
if (fieldKey === 'history') {
if (value === 'meeting') {
return 'bg-purple-100 text-purple-800 dark:bg-purple-900/50 dark:text-purple-300';
}
if (value === 'email_outbound') {
return 'bg-blue-100 text-blue-800 dark:bg-blue-900/50 dark:text-blue-300';
}
if (value === 'email_inbound') {
return 'bg-green-100 text-green-800 dark:bg-green-900/50 dark:text-green-300';
}
}
const option = getEnumOption(value, fieldKey, enumOptions);
if (option) {
return getColorClass(option.color);
}
// Fallback to gray if no option found
return getColorClass('gray');
}
/**
* Sorts an array of enum options by their enumOrder property.
*
* @param options - Array of enum options to sort
* @returns Sorted array of options
*/
export function sortEnumOptions(options: CrmFieldEnumOption[]): CrmFieldEnumOption[] {
return [...options].sort((a, b) => a.enumOrder - b.enumOrder);
}
export function getEnumTextColor(
value: string | null | undefined,
fieldKey=[redacted],
enumOptions?: CrmFieldEnumOption[],
): string {
if (value === null || value === undefined) return 'text-gray-600 dark:text-gray-400';
const option = getEnumOption(value, fieldKey, enumOptions);
if (option) return getTextColorClass(option.color);
return 'text-gray-600 dark:text-gray-400';
}