feat: enhance confirmation dialogs and form error handling; add new utility functions

This commit is contained in:
Firman Ramdhani
2026-07-24 16:21:27 +07:00
parent b7e9af9b07
commit b332da4808
10 changed files with 117 additions and 73 deletions
@@ -63,6 +63,11 @@ export const ACTION_TRANSLATION_MAP: Record<string, { titleKey: string; confirmK
confirmKey: 'common:actions.hold',
descriptionKey: 'common:confirmDialog.hold.description',
},
[ModuleAction.SAVE]: {
titleKey: 'common:confirmDialog.save.title',
confirmKey: 'common:actions.save',
descriptionKey: 'common:confirmDialog.save.description',
},
};
// ---------------------------------------------------------------------------
@@ -190,53 +190,6 @@ export interface TranslationSlice {
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
// ---------------------------------------------------------------------------
@@ -336,10 +289,46 @@ export interface BulkActionResult {
messages?: string[];
}
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;
}
// ---------------------------------------------------------------------------
// Page-Level Configurations
// ---------------------------------------------------------------------------
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 EnterpriseDetailPageConfig<E extends BaseEntity = BaseEntity> extends BasePageConfig {
editMode?: 'FULL' | 'PARTIAL';
customPageActions?: (data: E, actions: PageActionsProps['actions']) => PageActionsProps['actions'];
onDetailLoaded?: (data: E) => void;
onDataLoaded?: (data: E) => void;
showHighlightData?: boolean;
showHighlightDataOnBreadcrumbs?: boolean;
@@ -375,24 +364,24 @@ export interface EnterpriseDetailPageConfig<E extends BaseEntity = BaseEntity> e
holdModalConfig?: ActionModalConfig;
}
export interface PrivilegeEntity {
ALLOW_VIEW: boolean;
ALLOW_CREATE: boolean;
ALLOW_EDIT: boolean;
ALLOW_DELETE: boolean;
export interface EnterpriseFormPageConfig<E extends BaseEntity = BaseEntity> extends BasePageConfig {
formPageType: FormPageType;
customPageActions?: (data: E, actions: PageActionsProps['actions']) => PageActionsProps['actions'];
onDataLoaded?: (data: E) => void;
ALLOW_PRINT: boolean;
ALLOW_PRINT_COPY: boolean;
showHighlightData?: boolean;
showHighlightDataOnBreadcrumbs?: boolean;
highlightDataKey?: string;
ALLOW_APPROVAL: boolean;
ALLOW_ACTIVATE: boolean;
ALLOW_DEACTIVATE: boolean;
/** 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>;
ALLOW_CONFIRM: boolean;
ALLOW_CANCEL: boolean;
ALLOW_ROLLBACK: boolean;
ALLOW_HOLD: boolean;
ALLOW_LOGS: boolean;
ALLOW_NOTES: boolean;
/** 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)[];
initialValueCreate?: Partial<E>;
presetDuplicate?(payload: any): Promise<any>;
}
@@ -20,17 +20,17 @@ import { shortcutsData } from '../../../constants';
export function EnterpriseDetailPageProvider<E extends BaseEntity = BaseEntity>(props: EnterpriseDetailPageConfig<E>) {
const {
children,
editMode = 'FULL',
pageHeaderProps,
px,
py,
editMode = 'FULL',
showHighlightData = true,
showHighlightDataOnBreadcrumbs = true,
highlightDataKey = 'code',
statusKey = 'status',
getCustomStatusBadgeConfig,
onDetailLoaded,
onDataLoaded,
customPageActions,
onClickCreate,
@@ -77,7 +77,7 @@ export function EnterpriseDetailPageProvider<E extends BaseEntity = BaseEntity>(
if (response && response.data) {
const data = response.data?.data;
setDetailData(data as E);
if (onDetailLoaded) onDetailLoaded(data as E);
if (onDataLoaded) onDataLoaded(data as E);
}
} catch (error: any) {
notifications.show({
@@ -377,7 +377,7 @@ export function EnterpriseDetailPageProvider<E extends BaseEntity = BaseEntity>(
/** Platform-aware shortcut label for the Create action. */
const CREATE_SHORTCUT_LABEL = useMemo(() => {
const shortcutData = shortcutsData.find((s) => s.key === 'collapse_sidebar');
const shortcutData = shortcutsData.find((s) => s.key === 'create_data');
return IS_MACOS ? shortcutData?.macKeyIcons.join(' ') : shortcutData?.winKeyIcons.join(' ');
}, [IS_MACOS]);
@@ -37,7 +37,7 @@ export function EnterpriseIndexPageProvider(props: EnterpriseIndexPageConfig) {
/** Platform-aware shortcut label for the Create action. */
const CREATE_SHORTCUT_LABEL = useMemo(() => {
const shortcutData = shortcutsData.find((s) => s.key === 'collapse_sidebar');
const shortcutData = shortcutsData.find((s) => s.key === 'create_data');
return IS_MACOS ? shortcutData?.macKeyIcons.join(' ') : shortcutData?.winKeyIcons.join(' ');
}, [IS_MACOS]);