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 { _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 { config: ModuleConfigEntity; privileges: PrivilegeEntity; IS_MACOS: boolean; } export interface DataServiceSlice< E extends BaseEntity, S extends BaseRemoteDataServices = BaseRemoteDataServices, > { 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, TMeta = Record, > { 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, TMeta = Record, > { 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; } // --------------------------------------------------------------------------- // Page-Level Configurations // --------------------------------------------------------------------------- /** * Lifecycle interceptors for form processing. * @template E The base database entity. * @template TFormData The payload structure (defaults to Partial for DTOs). */ export interface EnterpriseFormLifecycleHooks> { onValidate?: (data: TFormData) => Promise; beforeSave?: (data: TFormData) => Promise; /** Overrides the default repository save implementation. */ save?: (data: TFormData) => Promise; afterSave?: (result: E) => Promise; afterGetData?: (data: E) => Promise; } interface BasePageConfig { children?: ReactNode; px?: string | number; py?: string | number; pageHeaderProps?: Omit; } export interface EnterpriseIndexPageConfig extends BasePageConfig { customPageActions?: (actions: PageActionsProps['actions']) => PageActionsProps['actions']; onClickCreate?: (key: string) => void; } export interface EnterpriseFormPageConfig extends EnterpriseFormLifecycleHooks { children?: ReactNode; showPageHeader?: boolean; useDefaultPadding?: boolean; customHiddenActions?: (data: Partial, 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; presetDuplicate?: (data: E) => Promise>; } // --------------------------------------------------------------------------- // 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). */ export interface ActionModalConfig = Record> { /** 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` * instance. Use `Field*` components from `@repo/ui/form` bound to this form. * * @example * ```tsx * // Static body (no form) * renderBody: () => Are you sure you want to delete this item? * * // Form body * renderBody: (form) => ( * * ) * ``` */ renderBody?: (form?: UseFormReturn) => ReactNode; /** Zod schema for validating the form body. When omitted, no validation is applied */ schema?: ZodType; /** 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 { opened: boolean; action: ModuleActionType | null; data: E | null; config?: ActionModalConfig; } export interface EnterpriseDetailPageConfig 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; 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; }