/** * Component prop types for @siesa/master-pattern-view * * These types define the public props API for the MasterPatternView component * and its sub-components. * * @module props.types * @see FR-001, FR-007, FR-010 */ import type { ReactNode } from 'react'; import type { MasterPatternViewDefinition, MasterPatternViewTabConfig } from './definition.types'; import type { MasterPatternViewEntity, MasterPatternViewService, MasterPatternViewCompany, ConfigTableService, ConfigSaveRow } from './service.types'; import type { MasterPatternViewPermissions, MasterPatternViewActionType } from './permissions.types'; import type { AuditHistoryProps } from '../../../audit/types/audit.types'; /** * Pagination configuration for `MasterPatternView`. * * Pass `false` to the `pagination` prop to disable pagination entirely. * * @see FR-005 — Pagination modes * * @example * ```tsx * // Classic page-based * pagination={{ mode: 'pages', defaultPageSize: 20, pageSizeOptions: [10, 20, 50] }} * * // Infinite scroll — pages accumulate as the user scrolls down * pagination={{ mode: 'infinite', defaultPageSize: 10 }} * * // No pagination * pagination={false} * ``` */ export interface PaginationConfig { /** * Pagination mode. * - `'pages'` — classic page controls (Previous / Next + page size selector) * - `'infinite'` — rows are appended as the user scrolls to the bottom of the container; * the service `getAll` is called with incrementing `page` numbers until all data is loaded */ mode: 'pages' | 'infinite'; /** Initial number of rows per page. Defaults to `20`. */ defaultPageSize?: number; /** Options shown in the page-size selector (e.g. `[10, 20, 50, 100]`). Only applies when `mode === 'pages'`. */ pageSizeOptions?: number[]; /** * Height of the scroll container in infinite-scroll mode. * Accepts any valid CSS value (e.g. `'400px'`, `'60vh'`). * Only used when `mode === 'infinite'`. */ tableHeight?: string | number; } /** * Batch action configuration for multi-row operations. * * @see FR-009 — Batch actions * * @param key - Unique identifier for the action * @param label - Display label * @param icon - Optional React node for the action icon * @param onExecute - Callback with selected rows * @param isDisabled - Optional function to conditionally disable */ export interface MasterPatternViewBatchAction { key: string; label: string; icon?: ReactNode; onExecute: (selectedRows: T[]) => void | Promise; isDisabled?: (selectedRows: T[]) => boolean; } /** * Custom action configuration for toolbar or row-level actions. * * @see FR-009 — Custom actions * * @param key - Unique identifier for the action * @param label - Display label * @param icon - Optional React node for the action icon * @param onExecute - Callback (receives selected row for row actions, undefined for toolbar) * @param isDisabled - Optional function to conditionally disable * @param position - Where to render the action */ export interface MasterPatternViewCustomAction { key: string; label: string; icon?: ReactNode; onExecute: (row?: T) => void | Promise; isDisabled?: (row?: T) => boolean; position?: 'toolbar' | 'row'; } /** * Visibility and disabled configuration for a single default row action. * * Both `show` and `disabled` accept a static boolean or a per-row function, * allowing conditional control at the row level. * * @param show - Whether to render the button. Defaults to `true`. * @param disabled - Whether to disable the button. Defaults to `false`. */ export interface MasterPatternViewDefaultActionConfig { show?: boolean | ((row: T) => boolean); disabled?: boolean | ((row: T) => boolean); } /** * Per-action visibility and disabled configuration for the built-in default row actions * (Visualizar, Editar, Cambiar Estado). * * Pass to the `defaultActions` prop on `MasterPatternView`. * * @example * ```tsx * // Hide the edit button for inactive records * defaultActions={{ * edit: { show: (row) => row.isActive }, * changeStatus: { disabled: (row) => !row.canChangeStatus }, * }} * ``` */ export interface MasterPatternViewDefaultActions { create?: MasterPatternViewDefaultActionConfig; view?: MasterPatternViewDefaultActionConfig; edit?: MasterPatternViewDefaultActionConfig; changeStatus?: MasterPatternViewDefaultActionConfig; } /** * Detail tab configuration for master-detail relationships. * Detail tabs appear after the form tabs and are hidden during Create mode. * * @see FR-010 — Master-Detail tabs * * @param key - Unique identifier for the detail tab * @param label - Display label for the tab * @param render - Render function receiving the parent entity * @param isVisible - Optional function to conditionally show/hide the tab */ /** Context injected by MasterPatternViewForm into every detail tab render call. */ export interface MasterPatternViewDetailTabContext { /** Whether "Mostrar datos globales" is active. Gates override indicators. */ isShowGlobalValuesEnabled: boolean; /** Global baseline values keyed by field name. Null outside company context. */ globalValues: Record | null; /** Fields the user has explicitly reverted to global during this edit session. */ revertedFields: string[]; /** Call when a field is reverted to global — registers it in the MPV store so the backend sends null. */ onFieldReverted: (fieldName: string) => void; } export interface MasterPatternViewDetailTabConfig { key: string; label: string; render: (parentEntity: T, formMode: 'creating' | 'editing' | 'viewing', context: MasterPatternViewDetailTabContext) => ReactNode; isVisible?: (parentEntity: T) => boolean; } /** * Form props passed to the renderForm escape hatch. * Consumers implementing custom form rendering receive this object. * * @see FR-007 — renderForm escape hatch * * @param values - Current form values * @param errors - Current validation errors (field name -> message) * @param isDirty - Whether the form has unsaved changes * @param submit - Function to programmatically submit the form * @param mode - Current form mode */ export interface MasterPatternViewFormProps { values: Partial; errors: Record; isDirty: boolean; submit: () => void; mode: 'creating' | 'editing' | 'viewing'; } /** * Imperative ref API for MasterPatternView. * Allows consumers to control the component programmatically. * * @see FR-013 — Imperative ref API (MasterPatternViewRef) * * @example * ```typescript * const ref = useRef(null); * ref.current?.openCreate(); * ref.current?.refresh(); * ``` */ export interface MasterPatternViewRef { /** Clears dirty state without closing the form */ resetDirty: () => void; /** Re-invokes getAll with current params */ refresh: () => void; /** Opens the form in Create mode */ openCreate: () => void; /** Opens the form in Edit mode for the given entity ID */ openEdit: (id: string) => void; /** Closes the form (respects dirty state dialog) */ closeForm: () => void; } /** * Context injected by MasterPatternView into `renderConfigBody` when * `definition.type === 'CONFIG_TABLE'`. * * The component owns loading, saving, and company-context state. * The consumer renders the grid/table and calls `onSave` to persist. */ export interface ConfigBodyContext { /** All rows to display (source rows merged with existing assignments). */ rows: TRow[]; /** Whether a company (non-global) context is active. */ isCompanyMode: boolean; /** Whether "Mostrar datos globales" toggle is ON. Driven by Acciones menu. */ showGlobalValues: boolean; /** * Global baseline rows keyed by `rowId`. * Populated only when `showGlobalValues === true` and `companyBehavior === 'GLOBAL'`. */ globalData: Map; /** Whether a save is in progress. */ isSaving: boolean; /** Call to persist changed rows. Only pass rows where `isDirty: true`. */ onSave: (rows: ConfigSaveRow[]) => void; /** Revert a single row to its global value (sets local state from `globalData`). */ onRestoreRow: (rowId: string) => void; /** Revert all rows to their global values. */ onRestoreAll: () => void; } /** * Context injected by MasterPatternView into `renderBody` when * `definition.type === 'CUSTOM'`. * * The component provides company-context state and save state; the consumer owns all data fetching. */ export interface CustomBodyContext { /** Available companies passed via the `companies` prop. */ companies: MasterPatternViewCompany[]; /** Active company ID, or `undefined` when in global context. */ companyId: string | undefined; /** Whether a company (non-global) context is active. */ isCompanyMode: boolean; /** * Whether a save is currently in progress. * Driven by `customSave.onSave`. Useful to disable inputs while saving. */ isSaving: boolean; /** Switch the active company context. Pass `undefined` to return to global. */ setCompanyId: (id: string | undefined) => void; } /** * Declarative save button configuration for `CUSTOM` type. * When provided, MasterPatternView renders a Guardar button in the toolbar * and manages the full save lifecycle (before → save → after). */ export interface CustomSaveConfig { /** Button label. Default: `'Guardar'` */ label?: string; /** Label shown while the save is in progress. Default: `'Guardando…'` */ savingLabel?: string; /** Hide the button entirely. Useful to conditionally remove it without clearing the prop. */ hidden?: boolean; /** * Disable the button without hiding it (e.g., when there are no unsaved changes). * The button remains visible but unclickable. */ disabled?: boolean; /** * Called before the save executes. * Return or resolve `false` to cancel the save (e.g., when validation fails). */ onBeforeSave?: () => boolean | Promise; /** The actual save action. Called only if `onBeforeSave` did not return `false`. */ onSave: () => void | Promise; /** Called after `onSave` completes successfully. Use for toasts, refreshes, etc. */ onAfterSave?: () => void | Promise; } /** * Root component props for MasterPatternView. * This is the primary public API surface. * * @see FR-001 — MasterPatternViewProps definition */ export interface MasterPatternViewProps { /** * Master definition — single source of truth for fields, tabs, sections, and master type. * * **`definition.type` options:** * - `'UNIVERSAL'` — global data with no company context; all users share the same records * - `'GLOBAL'` — global records that can be individually enabled and overridden per company * - `'COMPANY_SPECIFIC'` — each company manages its own independent set of records * - `'CONFIG_TABLE'` — bulk config table; rows from external source, inline editing, bulk save * - `'CUSTOM'` — shell only (toolbar + company context); full body rendered via `renderBody` * * See `MasterPatternViewDefinition` for the full field definition structure. */ definition: MasterPatternViewDefinition; /** * Service contract — the only communication channel between the component and the backend. * **Not used** when `definition.type` is `'CONFIG_TABLE'` or `'CUSTOM'` — use `configService` * or `renderBody` instead. * * **Required methods:** `getAll`, `create`, `update` * * **Optional methods:** * `getById`, `delete`, `changeStatus`, * `getOverrides`, `saveOverride`, `companyEnable`, `uncompanyEnable`, * `getChildren`, `getTree`, `moveNode`, `export`, `import` * * The component is fully backend-agnostic; it delegates all data operations to this interface. */ service?: MasterPatternViewService; /** Display title shown in the component header. */ title: string; /** * Human-readable name for the managed entity. * Used in confirmation dialogs, toast messages, and placeholders * (e.g. `"Moneda"`, `"Centro de Costo"`). * Optional for `CONFIG_TABLE` and `CUSTOM` types (no dialogs). */ entityName?: string; /** * Service for `CONFIG_TABLE` type. * Provides bulk data loading and saving; replaces `service` for this type. * Required when `definition.type === 'CONFIG_TABLE'`. */ configService?: ConfigTableService; /** * Renders the config table body when `definition.type === 'CONFIG_TABLE'`. * Receives `ConfigBodyContext` with rows, save handler, and company context. * Required when `definition.type === 'CONFIG_TABLE'`. */ renderConfigBody?: (ctx: ConfigBodyContext) => ReactNode; /** * Renders the full body when `definition.type === 'CUSTOM'`. * Receives only company context; the consumer owns all state and data fetching. * Required when `definition.type === 'CUSTOM'`. */ renderBody?: (ctx: CustomBodyContext) => ReactNode; /** * Renders extra buttons in the toolbar row, to the right of the Acciones dropdown. * * Available for both `CONFIG_TABLE` and `CUSTOM` types. * - For `CONFIG_TABLE`: ctx is `ConfigBodyContext` — access `ctx.isSaving`, `ctx.onSave`, etc. * - For `CUSTOM`: ctx is `CustomBodyContext` — access `ctx.isSaving`, `ctx.companyId`, etc. */ renderToolbarActions?: (ctx: any) => ReactNode; /** * Renders custom items inside the **Acciones** dropdown. * * Available for both `CONFIG_TABLE` and `CUSTOM` types. * - For `CONFIG_TABLE`: built-in items (`GLOBAL` behavior) appear first; custom items follow with a separator. * - For `CUSTOM`: the dropdown appears only when this prop is provided (no built-in items). * * Render ` * )} * ``` */ renderActionsMenuItems?: (ctx: any) => ReactNode; /** * Whether to show the built-in company selector in the toolbar for `CUSTOM` type. * * - `true` *(default when `companyBehavior !== 'NONE'`)* — shows the `CompanySelector` above the body. * - `false` — hides it; render your own selector inside `renderBody` using `ctx.companies` and `ctx.setCompanyId`. * * Has no effect on `CONFIG_TABLE` (always shown when `companyBehavior !== 'NONE'`). */ showCompanySelector?: boolean; /** * Declarative save button for `CUSTOM` type. * * When provided, MasterPatternView renders a **Guardar** button in the toolbar * and manages the full save lifecycle: * `onBeforeSave` (optional guard) → `onSave` → `onAfterSave` (optional callback). * * `ctx.isSaving` in `renderBody` reflects the in-progress state so you can disable inputs. * * @example * ```tsx * customSave={{ * label: 'Aplicar', * disabled: !isDirty, * onBeforeSave: () => validate(), // return false to cancel * onSave: handleSave, * onAfterSave: () => setIsDirty(false), * }} * ``` */ customSave?: CustomSaveConfig; headerFields?: string[]; tabs?: MasterPatternViewTabConfig[]; /** * Form navigation mode. Controls how the form appears relative to the list. * * - `'sidebar'` *(default)* — slides in as a right-side panel; the list stays visible behind an overlay * - `'modal'` — centered dialog with dark overlay; focuses the user exclusively on the form * - `'page'` — replaces the list entirely; ideal for forms with many fields or sub-tabs */ navigationType?: 'modal' | 'sidebar' | 'page'; /** * Whether the `navigationType='page'` form shows its own internal "Volver" back-nav link. * Ignored for `'modal'`/`'sidebar'` (they close via backdrop/X instead, no such link exists). * Set to `false` when the host embeds this page-mode form somewhere with no "back" to go * to — e.g. inside its own popup/route with `openRecordMode='creating'` — since there's * nothing for that link to sensibly do there. * @default true */ showBackButton?: boolean; /** * Width of the form panel. Accepts any valid CSS value (e.g. `'480px'`, `'40%'`, `'50vw'`). * Only applies when `navigationType='sidebar'`. */ formWidth?: string; /** * Maximum height of the form modal. Accepts any valid CSS value (e.g. `'90vh'`, `'600px'`). * Only applies when `navigationType='modal'`. Defaults to `'90vh'`. */ formHeight?: string; /** * Controlled deep-link: id of a record to auto-open on mount (or whenever this value changes). * Enables sharing a direct link to a record's detail — the host app reads the id from its * own router/URL and passes it here; MasterPatternView looks it up (from the loaded list, * falling back to `service.getById`) and opens it via `openRecordMode`. * Set to `null`/`undefined` to leave the list view active. */ openRecordId?: string | null; /** * Form mode used when auto-opening `openRecordId`. Defaults to `'editing'`. * * `'creating'` is a distinct case: it does NOT depend on `openRecordId` at all (there's * no record to look up yet) — as soon as this is `'creating'`, the create form opens on * mount via the same internal path the toolbar "Crear" button uses. Useful for a * route-based flow (e.g. a host app's `/entity/new` URL) where the same list page * component is reused and just told to open straight into creation instead of the list. */ openRecordMode?: 'editing' | 'viewing' | 'creating'; /** * Called whenever the form panel opens — from a row action, the toolbar "Crear" button, * or the `openRecordId`/`openRecordMode='creating'` deep-link. Use this to push the * record's id onto the host app's URL so the detail view becomes shareable. */ onRecordOpen?: (record: T | null, mode: 'creating' | 'editing' | 'viewing') => void; /** Called whenever the form panel closes. Use this to clear the record id from the URL. */ onRecordClose?: () => void; /** * Called right after the DEFAULT save flow succeeds — i.e. after the internal * `service.create()`/`service.update()` call MasterPatternView makes for you resolves. * Unlike `customSave.onAfterSave` (which only runs when you've fully taken over saving), * this fires for masters that use the standard, built-in save path. Fires in addition to * — not instead of — the existing `onCreateSuccess`/`onUpdateSuccess` callbacks. */ onRecordSaved?: (record: T, mode: 'creating' | 'editing') => void; /** * Pagination configuration. Pass `false` to disable pagination entirely. * * **`PaginationConfig` fields:** * - `mode: 'pages'` — classic Previous / Next controls with a page-size selector * - `mode: 'infinite'` — rows are appended as the user scrolls to the bottom; * `service.getAll` is called with incrementing `page` numbers until exhausted * - `defaultPageSize` — initial rows per page (default: `20`) * - `pageSizeOptions` — choices in the page-size selector (e.g. `[10, 20, 50, 100]`) * - `tableHeight` — container height for infinite mode (e.g. `'400px'`, `'60vh'`); * only used when `mode === 'infinite'` */ pagination?: PaginationConfig | false; /** Enables per-row checkboxes for multi-row selection. Required to use `batchActions`. */ selectable?: boolean; batchActions?: MasterPatternViewBatchAction[]; /** * When `true`, triggers the initial data load on mount without requiring user interaction. * Useful when `defaultFiltersOpen` is `false` or when records should be visible immediately. */ autoLoad?: boolean; /** * Unique cache isolation key. * When provided, this MPV instance uses its own React Query cache instead of the * shared module-level client. Required when two MPV instances with the same * `entityName` are rendered simultaneously (e.g., a child list inside a `detailTab`). * Omit for all existing single-instance usages — behavior is unchanged. */ cacheKey?: string; /** * When `true`, the advanced filter panel is rendered open on mount. * Combine with `autoLoad: false` to require the user to set filters before loading data. */ defaultFiltersOpen?: boolean; /** * When `true`, shows a table / cards toggle widget in the toolbar. * Allows the user to switch between the default table view and a card grid. * @default true */ showViewToggle?: boolean; /** * Custom render function for card content in Cards view mode. * When provided, replaces the auto-generated `CardContent` body for each record. * The `CardHeader` (with title, subtitle, and action buttons) and `CardFooter` * are still rendered automatically around the custom content. * * @param row - The data record for this card * @param index - Zero-based index of the card in the current page */ renderCardContent?: (row: T, index: number) => ReactNode; /** * Shows the export button in the toolbar. * Requires `service.export` to be implemented. */ showExport?: boolean; /** * Shows the import button in the toolbar. * Requires `service.import` to be implemented. */ showImport?: boolean; onExport?: () => void; onImport?: () => void; globalActions?: MasterPatternViewCustomAction[]; /** * Enables the "Auditoría de Cambios" action (row kebab + form "Acciones" menu), * mirroring MasterCrud's `auditHistory` prop. Opens a modal with `AuditHistoryPanel` * showing the change history for the selected record. Omit or set `enabled: false` * to hide it entirely (NOT NEVER-HIDE — this is an opt-in feature, not a permission). */ auditHistory?: AuditHistoryProps; detailTabs?: MasterPatternViewDetailTabConfig[]; /** * Enables hierarchical tree mode. * Requires `service.getChildren` or `service.getTree` to be implemented. */ hierarchical?: boolean; /** * Maximum depth level for the tree. `0` = root nodes only. * Defaults to unlimited if not specified. */ hierarchicalDepth?: number; /** * List of available companies. Required when `definition.type` is `'GLOBAL'` or `'COMPANY_SPECIFIC'`. * Activates the company selector in the toolbar. */ companies?: MasterPatternViewCompany[]; /** * Company selected on mount. Falls back to the first entry in `companies` if not provided. */ defaultCompany?: MasterPatternViewCompany; /** Callback fired when the user changes the active company in the toolbar selector. */ onCompanyChange?: (company: MasterPatternViewCompany) => void; /** * Permission configuration per action. All keys default to `true` if the object is omitted. * * **NEVER-HIDE invariant:** buttons are always rendered and enabled regardless of permissions. * Clicking a restricted action shows a warning toast instead of being silently disabled. * * **Available keys:** * - `canCreate` — "+ Crear" toolbar button * - `canRead` — view record details * - `canUpdate` — edit records * - `canDelete` — delete records * - `canChangeStatus` — toggle active / inactive status * - `canUpdateGlobal` — edit global fields (only relevant for `GLOBAL` type) * - `canCompanyEnable` — enable / disable the entity per company (only relevant for `GLOBAL` type) */ permissions?: MasterPatternViewPermissions; /** Custom synchronous validation function. Returns a `{ fieldName: errorMessage }` map. */ validate?: (values: Partial) => Record; /** * Fully custom form renderer. When provided, replaces the auto-generated form. * Receives `{ values, errors, isDirty, submit, mode }`. */ renderForm?: (props: MasterPatternViewFormProps) => ReactNode; /** * Fields used to build the form title when viewing or editing a record. * Non-empty values are joined with `" - "`. * * When omitted, the component falls back to the `code → name` convention automatically. * * @example * ```tsx * // Shows "C001 - Acme Corp" when both fields have values * recordTitleFields={['code', 'name']} * * // Shows "Acme Corp (Colombia)" — three fields with custom separator in value * recordTitleFields={['name', 'countryName']} * ``` */ recordTitleFields?: string[]; /** Default values pre-filled when opening the Create form. Merged with definition defaults. */ defaultCreateValues?: Record; rowActions?: (row: T) => ReactNode; actions?: MasterPatternViewCustomAction[]; /** * Visibility and disabled overrides for the built-in default row actions * (Visualizar, Editar, Cambiar Estado). * * Each key corresponds to one default action. Both `show` and `disabled` * accept a static boolean or a per-row function `(row: T) => boolean`. * * - `show: false` — removes the button from the DOM entirely. * - `disabled: true` — renders the button as truly disabled (not just a toast). * * The existing `permissions` prop still controls the toast behavior for allowed/denied * actions independently of this prop. */ defaultActions?: MasterPatternViewDefaultActions; /** * Determines whether the active record can be edited when the form is in view mode. * * Return values: * - `true` — allow (Editar button shown and functional). * - `false` — **ocultar**: the Editar button is hidden entirely. * - `string` — **mostrar mensaje**: the button remains visible; clicking * it shows a warning toast with the returned string. * - `{ disabled: true }` — **inhabilitar**: the button is shown but grayed out and * not clickable. * - `{ disabled: true, reason: s }` — **inhabilitar con tooltip**: same as above, but `reason` * appears as a tooltip when the user hovers the button. * * Use this for records that are conditionally read-only (e.g. internal or system records). * To also control the edit button in the list row, combine with `defaultActions.edit`. * * @example * ```tsx * // Ocultar — hide silently * canEdit={(record) => !record.isInternal} * * // Inhabilitar — grayed out with tooltip * canEdit={(record) => * record.isInternal * ? { disabled: true, reason: 'Los registros internos no pueden editarse.' } * : true * } * * // Mostrar mensaje — toast on click * canEdit={(record) => * record.isInternal * ? 'Este registro es interno y no puede modificarse.' * : true * } * ``` */ canEdit?: (record: T) => boolean | string | { disabled: true; reason?: string; }; /** Fired after a record is successfully created. Receives the new entity. */ onCreateSuccess?: (record: T) => void; /** Fired after a record is successfully updated. Receives the updated entity. */ onUpdateSuccess?: (record: T) => void; /** Fired after a record is successfully deleted. Receives the deleted entity's `id`. */ onDeleteSuccess?: (id: string) => void; /** Fired after a status change (active ↔ inactive). Receives the updated entity. */ onStatusChangeSuccess?: (record: T) => void; /** * Global error handler. Receives the error and the action type that triggered it * (`'CREATE'`, `'UPDATE'`, `'DELETE'`, `'CHANGE_STATUS'`, etc.). */ onError?: (error: Error, action: MasterPatternViewActionType) => void; /** * Offline persistence interceptor. When provided, replaces `service.create` / `service.update` * after validation passes. The consumer owns the API call and its timing. * * **MPV guarantees during the call:** * - Save button is disabled (`isSubmitting = true`) until the promise settles. * - On resolve → form closes (`closeForm()`). React Query cache is NOT invalidated. * - On reject → error is forwarded to `onError`; form stays open. * * **Invariant:** omitting this prop preserves today's exact behavior (service.create/update path * unchanged). The validation pipeline always runs regardless of whether this prop is present. * * **Never** provide this prop for `CONFIG_TABLE` or `CUSTOM` definition types — they already * have `customSave` / `renderBody` for consumer-controlled persistence. * * @param mode - `'creating'` or `'editing'` * @param values - Validated form values * @param originalRecord - The record being edited (`undefined` when creating) */ onFormSave?: (mode: 'creating' | 'editing', values: Record, originalRecord?: T) => void | Promise; /** * External dirty-state signal. When `true`, the MPV treats the form as dirty even if * react-hook-form's own `isDirty` is `false`. This causes the `DirtyStateDialog` to appear * when the user attempts to cancel or close the form. * * Use case: a child list inside a `detailTab` has pending offline changes (accumulated via * `onFormSave`). The parent passes `externalIsDirty={true}` to prevent the user from * accidentally discarding those changes when closing the parent form. * * **Invariant:** omitting this prop (or passing `false`) leaves dirty detection unchanged — * only react-hook-form's internal `isDirty` is evaluated. */ externalIsDirty?: boolean; /** External loading flag. When `true`, shows the loading skeleton regardless of internal state. */ isLoading?: boolean; /** Locale code for i18n (e.g. `'es-CO'`, `'en-US'`). */ locale?: string; /** Translation function override. Falls back to the built-in Spanish strings if not provided. */ t?: (key: string, defaultValue?: string) => string; /** * Prefix used to namespace UI translation keys when resolved through the global i18n system * (e.g. `'masterpatternview.create'`). * @default 'masterpatternview' */ uiPrefix?: string; /** * Prefix used to namespace entity-field translation keys, tried as a secondary lookup when * the UI-prefixed key is not found. */ fieldsPrefix?: string; /** * Static dictionary of translation overrides, checked before the internal Spanish/English * fallback. */ externalTranslations?: Record; /** * External toast function. When provided, replaces the component's internal toast system. * Signature: `(type: 'success' | 'error' | 'warning' | 'info', message: string) => void` */ toast?: (type: 'success' | 'error' | 'warning' | 'info', message: string) => void; /** * Whether to render the kit's own toast UI for internal events (validation errors, * create/update/company-enable results, etc). The `mpv:toast` window event fires * regardless of this flag — set to `false` when the host app already listens for * `mpv:toast` and shows its own toast, to avoid showing the same message twice. * @default true */ showInternalToasts?: boolean; } //# sourceMappingURL=props.types.d.ts.map