feat: add RowActions component for enhanced row-level actions in data grids
- Introduced RowActions component to manage row-level actions with tooltips and dropdown menus. - Created types for row actions and page actions to standardize action properties. - Implemented utility function to map action intents to Mantine theme colors. - Updated CoreAppShell component to support optional slots for better flexibility. - Added enterprise module structure with context hooks for managing module state and actions. - Implemented draft management for forms to enhance user experience during data entry. - Established context providers for detail, form, and index pages to streamline data handling. - Updated dependencies to ensure compatibility with the latest versions.
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
import { ReactNode } from 'react';
|
||||
import type { BaseEntity, BaseRemoteDataServices } from '@repo/core-api/data-services';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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',
|
||||
} 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';
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
moduleKey: string;
|
||||
webUrl: string;
|
||||
apiUrl: string;
|
||||
tabTitle?: string;
|
||||
moduleCategory: ModuleCategoryType;
|
||||
translationNamespace: string;
|
||||
|
||||
singlePageFormConfig?: SinglePageConfig;
|
||||
singlePageDetailConfig?: SinglePageConfig;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Context Slices (Anti-Rerender Strategy)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface ConfigSlice<E extends BaseEntity = BaseEntity> {
|
||||
config: ModuleConfigEntity<E>;
|
||||
}
|
||||
|
||||
export interface DataServiceSlice<
|
||||
E extends BaseEntity,
|
||||
S extends BaseRemoteDataServices<E> = BaseRemoteDataServices<E>,
|
||||
> {
|
||||
dataServices: S;
|
||||
/**
|
||||
* User privileges payload for the current module.
|
||||
* Typed as `unknown` to enforce strict type checking before consumption via RBAC utilities.
|
||||
*/
|
||||
privilege: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
setMetaData: (data: TMeta) => void;
|
||||
filterData: TFilter;
|
||||
setFilterData: (data: TFilter) => 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>;
|
||||
}
|
||||
|
||||
export interface EnterpriseIndexPageConfig<E extends BaseEntity = BaseEntity> {
|
||||
children?: ReactNode;
|
||||
showPageHeader?: boolean;
|
||||
useDefaultPadding?: boolean;
|
||||
customHiddenActions?: (selected: E[], defaultHidden: string[]) => string[];
|
||||
/** Strongly typed event handler to prevent arbitrary string usage for actions. */
|
||||
onCustomActionClick?: (key: ModuleActionType, data?: unknown) => void;
|
||||
filterDrawerContent?: ReactNode;
|
||||
|
||||
registerRefreshCallback?: (callback: () => void) => 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>>;
|
||||
}
|
||||
|
||||
export interface EnterpriseDetailPageConfig<E extends BaseEntity = BaseEntity> {
|
||||
children?: ReactNode;
|
||||
showPageHeader?: boolean;
|
||||
useDefaultPadding?: boolean;
|
||||
customHiddenActions?: (data: E | null, defaultHidden: string[]) => string[];
|
||||
onCustomActionClick?: (key: ModuleActionType, data?: unknown) => void;
|
||||
|
||||
editMode?: 'FULL' | 'PARTIAL';
|
||||
afterGetData?: (data: E) => Promise<unknown>;
|
||||
}
|
||||
Reference in New Issue
Block a user