Files
trackgo-fe/packages/ui/src/foundations/enterprise-module/entities/entity.ts
T

365 lines
13 KiB
TypeScript

import { ReactNode } from 'react';
import type { UseFormReturn } from 'react-hook-form';
import type { ZodType } from 'zod';
import type { BaseEntity, BaseRemoteDataServices } from '@repo/core-api/data-services';
import type { ModulePageHeaderProps } from '../components/module-page-header';
import { PageActionsProps } from '../../../components';
// ---------------------------------------------------------------------------
// Module Constants & Base Types
// ---------------------------------------------------------------------------
/**
* Standardized action constants across all enterprise modules.
* Used to define RBAC permissions, UI rendering logic, and event handling.
*/
export const ModuleAction = {
CREATE: 'CREATE',
EDIT: 'EDIT',
DELETE: 'DELETE',
DUPLICATE: 'DUPLICATE',
SAVE: 'SAVE',
PRINT: 'PRINT',
PRINT_COPY: 'PRINT_COPY',
APPROVAL: 'APPROVAL',
ACTIVATE: 'ACTIVATE',
DEACTIVATE: 'DEACTIVATE',
CONFIRM: 'CONFIRM',
CANCEL: 'CANCEL',
ROLLBACK: 'ROLLBACK',
HOLD: 'HOLD',
LOGS: 'LOGS',
NOTES: 'NOTES',
FILTER: 'FILTER',
CONFIG: 'CONFIG',
} as const;
/**
* Represents a valid module action.
* Utilizes the `(string & {})` pattern to preserve IDE autocomplete for standard actions
* while allowing implementers to extend it with custom string identifiers.
*/
// eslint-disable-next-line @typescript-eslint/ban-types
export type ModuleActionType = (typeof ModuleAction)[keyof typeof ModuleAction] | (string & {});
/**
* Defines the architectural category of the module (e.g., routed vs. modal-driven).
*/
export type ModuleCategoryType = 'SINGLE_PAGE' | 'FULL_PAGE';
type ModuleType = 'TRANSACTION' | 'MASTER_DATA';
/**
* Represents the current operational mode of a form instance.
*/
export type FormPageType = 'CREATE' | 'EDIT' | 'DUPLICATE';
export interface SinglePageConfig {
type?: 'MODAL' | 'DRAWER';
size?: string | number;
}
export interface SinglePageModalState {
open: boolean;
dataId?: string;
}
export interface SinglePageFormState extends SinglePageModalState {
formType: FormPageType;
}
export interface DraftConfig {
enableDraft?: boolean;
/** @default 5000 */
autoSaveIntervalMs?: number;
}
// ---------------------------------------------------------------------------
// Root Provider Configuration
// ---------------------------------------------------------------------------
/**
* Foundational configuration injected into the root ModuleProvider.
* @template E The base database entity for the module.
* @property moduleKey Unique identifier for the module, used for permissions, caching, and i18n.
* @property webUrl Base URL for the module's web routes.
* @property apiUrl Base API endpoint for the module's remote data services.
* @property tabTitle Optional title for browser tabs. When omitted, the Provider will auto-set it from the module's translation key `title`.
* @property moduleCategory Architectural category of the module, used for rendering and routing logic.
* @property translationNamespace Translation namespace for i18n resource bundles. Must match the namespace string used in `registerModuleNamespace()`.
* @property singlePageFormConfig Optional configuration for single-page form modals or drawers.
* @property singlePageDetailConfig Optional configuration for single-page detail modals or drawers.
*/
export interface ModuleConfigEntity<E extends BaseEntity = BaseEntity> {
_data?: E; // Fix unused generic
moduleKey: string;
webUrl: string;
apiUrl: string;
tabTitle?: string;
moduleCategory: ModuleCategoryType;
moduleType: ModuleType;
translationNamespace: string;
singlePageFormConfig?: SinglePageConfig;
singlePageDetailConfig?: SinglePageConfig;
}
// ---------------------------------------------------------------------------
// Context Slices (Anti-Rerender Strategy)
// ---------------------------------------------------------------------------
export interface ConfigSlice<E extends BaseEntity = BaseEntity> {
config: ModuleConfigEntity<E>;
privileges: PrivilegeEntity;
IS_MACOS: boolean;
}
export interface DataServiceSlice<
E extends BaseEntity,
S extends BaseRemoteDataServices<E> = BaseRemoteDataServices<E>,
> {
dataServices: S;
}
/**
* State management slice for grid/table selections and data filtering.
* Utilizes generic parameters to ensure type safety for arbitrary filter and metadata payloads.
*/
export interface SelectionSlice<
E extends BaseEntity = BaseEntity,
TFilter = Record<string, unknown>,
TMeta = Record<string, unknown>,
> {
selectedRows: E[];
setSelectedRows: (rows: E[]) => void;
metaData: TMeta | null;
setMetaData: (data: TMeta | null) => void;
filterData: TFilter | null;
setFilterData: (data: TFilter | null) => void;
}
/**
* Base generic state for Enterprise Module store using Zustand.
*/
export interface EnterpriseModuleState<
E extends BaseEntity = BaseEntity,
TFilter = Record<string, unknown>,
TMeta = Record<string, unknown>,
> {
metaData: TMeta | null;
setMetaData: (data: TMeta | null) => void;
filterData: TFilter | null;
setFilterData: (data: TFilter | null) => void;
selectedRows: E[];
setSelectedRows: (rows: E[]) => void;
privileges: string[];
setPrivileges: (privileges: string[]) => void;
}
export interface NavigationSlice {
navigateToIndex: () => void;
navigateToCreate: () => void;
navigateToEdit: (id: string) => void;
navigateToDetail: (id: string) => void;
navigateToDuplicate: (id: string) => void;
}
export interface ModalSlice {
formState: SinglePageFormState;
setFormState: (state: SinglePageFormState) => void;
detailState: SinglePageModalState;
setDetailState: (state: SinglePageModalState) => void;
}
/**
* Translation slice for enterprise modules, providing a standardized interface for i18n operations.
*
* The `t` function is scoped to `[moduleNamespace, 'common']`, meaning:
* - Keys are first resolved in the module's own namespace.
* - If not found, they fall back to the `common` namespace.
* - Explicit namespace prefix (e.g. `common:save`) is still supported.
*
* @property t - Translation function scoped to the module's namespace with common fallback.
*/
export interface TranslationSlice {
/** Translation function scoped to [moduleNamespace, 'common']. No prefix needed for module keys. */
t: (key: string, options?: Record<string, unknown>) => string;
}
// ---------------------------------------------------------------------------
// Page-Level Configurations
// ---------------------------------------------------------------------------
/**
* Lifecycle interceptors for form processing.
* @template E The base database entity.
* @template TFormData The payload structure (defaults to Partial<E> for DTOs).
*/
export interface EnterpriseFormLifecycleHooks<E extends BaseEntity, TFormData = Partial<E>> {
onValidate?: (data: TFormData) => Promise<boolean>;
beforeSave?: (data: TFormData) => Promise<TFormData>;
/** Overrides the default repository save implementation. */
save?: (data: TFormData) => Promise<E>;
afterSave?: (result: E) => Promise<void>;
afterGetData?: (data: E) => Promise<TFormData>;
}
interface BasePageConfig {
children?: ReactNode;
px?: string | number;
py?: string | number;
pageHeaderProps?: Omit<ModulePageHeaderProps, 'actions' | 'moduleKey'>;
}
export interface EnterpriseIndexPageConfig extends BasePageConfig {
customPageActions?: (actions: PageActionsProps['actions']) => PageActionsProps['actions'];
onClickCreate?: (key: string) => void;
}
export interface EnterpriseFormPageConfig<E extends BaseEntity = BaseEntity> extends EnterpriseFormLifecycleHooks<E> {
children?: ReactNode;
showPageHeader?: boolean;
useDefaultPadding?: boolean;
customHiddenActions?: (data: Partial<E>, defaultHidden: string[]) => string[];
/** Strongly typed event handler for custom form interactions. */
onCustomActionClick?: (key: ModuleActionType, data?: unknown) => void;
draftConfig?: DraftConfig;
/** Type-safe array of entity keys to exclude during an update operation. */
ignoreKeyUpdate?: (keyof E)[];
/** Type-safe array of entity keys to exclude when duplicating a record. */
ignoreKeyDuplicate?: (keyof E)[];
initialValue?: Partial<E>;
presetDuplicate?: (data: E) => Promise<Partial<E>>;
}
// ---------------------------------------------------------------------------
// Action Confirmation Modal Configuration
// ---------------------------------------------------------------------------
/**
* Configuration for the confirmation modal shown before executing a lifecycle action.
*
* Supports three modes:
* 1. **Simple confirmation** — no `renderBody`, just a confirm/cancel dialog.
* 2. **Static body** — `renderBody` returns static JSX (text, checkboxes, etc.).
* 3. **Form body** — `renderBody` receives an RHF `UseFormReturn` instance.
* The implementer renders `Field*` components bound to the form.
* Form data is validated via `schema` before submission and sent as `meta`.
*
* @template TMeta The shape of the form data (defaults to Record<string, unknown>).
*/
export interface ActionModalConfig<TMeta extends Record<string, unknown> = Record<string, unknown>> {
/** Modal title override. Defaults to action-specific translation (e.g., "Delete Item?") */
title?: string;
/**
* Custom modal body renderer.
*
* When provided WITHOUT `schema`/`defaultValues`, receives `undefined` — render static content.
* When provided WITH `schema`/`defaultValues`, receives a fully-typed `UseFormReturn<TMeta>`
* instance. Use `Field*` components from `@repo/ui/form` bound to this form.
*
* @example
* ```tsx
* // Static body (no form)
* renderBody: () => <Text>Are you sure you want to delete this item?</Text>
*
* // Form body
* renderBody: (form) => (
* <FieldTextarea name="reason" control={form.control} label="Reason" />
* )
* ```
*/
renderBody?: (form?: UseFormReturn<TMeta>) => ReactNode;
/** Zod schema for validating the form body. When omitted, no validation is applied */
schema?: ZodType<TMeta>;
/** Default values for the form. Required when schema is provided */
defaultValues?: TMeta;
/** Confirm button label override. Defaults to action translation */
confirmLabel?: string;
/** Cancel button label override. Defaults to t('common:actions.cancel') */
cancelLabel?: string;
/** Size of the modal. @default 'md' */
size?: string | number;
/** Custom success message or a function to generate it after action completes successfully */
successMessage?: string | ((data?: any) => string);
/** Custom error message or a function to extract it from the network error */
errorMessage?: string | ((error: any) => string);
}
/**
* Internal state for the action confirmation modal managed by the detail page provider.
* @internal
*/
export interface ActionModalState<E extends BaseEntity = BaseEntity> {
opened: boolean;
action: ModuleActionType | null;
data: E | null;
config?: ActionModalConfig<any>;
}
export interface EnterpriseDetailPageConfig<E extends BaseEntity = BaseEntity> extends BasePageConfig {
editMode?: 'FULL' | 'PARTIAL';
customPageActions?: (data: E, actions: PageActionsProps['actions']) => PageActionsProps['actions'];
onDetailLoaded?: (data: E) => void;
showHighlightData?: boolean;
showHighlightDataOnBreadcrumbs?: boolean;
highlightDataKey?: string;
/** Key to reference status data in the entity, used to render the status badge automatically. @default 'status' */
statusKey?: string;
/** Custom callback to provide dynamic status badge properties */
getCustomStatusBadgeConfig?: (status: string) => Partial<import('@mantine/core').BadgeProps>;
onClickCreate?: () => void;
onClickDuplicate?: (data: E) => void;
onClickEdit?: (data: E) => void;
onClickDelete?: (data: E) => void;
// Master Data Feature
onClickActivate?: (data: E) => void;
onClickDeactivate?: (data: E) => void;
// Transaction Feature
onClickConfirm?: (data: E) => void;
onClickCancel?: (data: E) => void;
onClickRollback?: (data: E) => void;
onClickHold?: (data: E) => void;
// Action Modal Configurations
deleteModalConfig?: ActionModalConfig;
activateModalConfig?: ActionModalConfig;
deactivateModalConfig?: ActionModalConfig;
confirmModalConfig?: ActionModalConfig;
cancelModalConfig?: ActionModalConfig;
rollbackModalConfig?: ActionModalConfig;
holdModalConfig?: ActionModalConfig;
}
export interface PrivilegeEntity {
ALLOW_VIEW: boolean;
ALLOW_CREATE: boolean;
ALLOW_EDIT: boolean;
ALLOW_DELETE: boolean;
ALLOW_PRINT: boolean;
ALLOW_PRINT_COPY: boolean;
ALLOW_APPROVAL: boolean;
ALLOW_ACTIVATE: boolean;
ALLOW_DEACTIVATE: boolean;
ALLOW_CONFIRM: boolean;
ALLOW_CANCEL: boolean;
ALLOW_ROLLBACK: boolean;
ALLOW_HOLD: boolean;
ALLOW_LOGS: boolean;
ALLOW_NOTES: boolean;
}