Skip to content

Convert "API Maker UI Maker" (JSON-config-driven grid + form) to hand-written Angular + PrimeNG

1. Title & Objective

You are an AI coding agent. Your task is to convert an API Maker "UI Maker" page β€” a single JSON configuration object (IDBMasterConfig, conventionally the variable dbMasterConfig) that drives a PrimeNG data grid plus add/edit/view reactive forms inside an iframe β€” into a normal, explicit, hand-written Angular + PrimeNG application: a p-table-based grid component and reactive FormGroup-based form components, where every feature currently expressed in JSON is implemented directly in Angular/PrimeNG TypeScript and templates.

Non-negotiable rule: ZERO FEATURE LOSS. Every form control, every property, every validation rule, every file-upload behavior, every dependent/cascading dropdown, every conditional show/hide, every dynamic-required rule, every nested field, every nested grid, every add-new-inline form, every API-call override, every CSV import/export behavior, every theme, every iframe/postMessage event, every row-level action-hiding flag, every custom action button, every grid grouping/sorting/pagination/selection/filter/search/column-selector behavior, and every operations-ordering rule must be reproduced exactly. Do not invent features that are not in the source config. Where the source config is ambiguous, inspect the original dbMasterConfig for that page and reproduce the observed runtime behavior.

Work page-by-page: for each UI Maker page you are given (identified by db-master-name), produce a dedicated Angular feature module/standalone component set. Read that page's dbMasterConfig in full first β€” it is the source of truth that overrides any general guidance below.


2. Background: What UI Maker is

UI Maker is an extension layered on top of API Maker. It takes one IDBMasterConfig object and renders, at runtime, a complete CRUD screen for a database table/collection:

  • A PrimeNG data grid (grid config) with columns, sorting, filtering, global search, pagination, grouping, row selection, row actions (edit/view/delete + custom), and a toolbar (add/import/export/refresh/column-selector/theme-selector/delete-all + custom actions).
  • Reactive add/edit/view forms (form.fields, a 2-D array: outer = rows, inner = columns) built from 24 control types (EDBMasterFormControl).
  • Each field is bound to a dot-notation path, supports static or JS-expression-string visible/disabled/requiredFun, can nest other fields (fields[][]) or a whole child grid (gridSettings).
  • Pages run inside an iframe and talk to the host via window.postMessage. They call API Maker auto-generated POST endpoints for all data operations, each overridable via apiCallOverrides.
  • Custom CSS (cssCode[{runOn:'header', code}]) and JS lifecycle hooks (jsCode[{appendTo, code}]) can be injected globally (page) or per control. Code can be inline strings, functions, or references to external named functions (userUtils.Main.* per-page, userUtils.GeneralUtils.* shared).
  • Inbuilt libraries available to custom code: Papa Parse (Papa), date-fns (dateFns), date-fns-tz (dateFnsTz); commonly added: lodash (_), moment. Inbuilt CSS: Bootstrap 5.3.3 grid + utilities.

UI Maker cannot run standalone β€” it is coupled to the API Maker backend that supplies data. Your conversion must therefore also implement an explicit Angular HttpClient service layer that calls the same backend endpoints.


3. Target architecture

Implement this structure (standalone Angular components, PrimeNG, ReactiveFormsModule):

src/app/
  ui-maker-shared/                  # shared, imported by EVERY converted page
    services/
      api.service.ts                # HttpClient layer; replaces all API Maker calls + apiCallOverrides
      query.service.ts              # builds API Maker IQueryFormat (find/select/limit/deep/sort) bodies
      parent-integration.service.ts # postMessage send/receive (iframe contract) OR @Input/@Output bridge
      expression.service.ts         # safe evaluator for visible/disabled/requiredFun JS-expression strings
      hook-runner.service.ts        # executes jsCode (string | function | userUtils.* ref) with $scope
      message.service.ts            # toast wrapper -> utils.messageService.*
      file-upload.service.ts        # upload/download/remove contract
      theme.service.ts              # theme switching
      date-format.service.ts        # date-fns <-> PrimeNG calendar format + tz conversion
    utils/
      general-utils.ts              # <= userUtils.GeneralUtils.* (shared common JS)
      form-path.util.ts             # dot-notation path <-> nested FormGroup helpers
    components/
      um-grid/                      # reusable generic p-table grid component (config-light, typed)
      um-form/                      # reusable form renderer OR base class for per-page forms
      controls/                     # one component per EDBMasterFormControl (24)
  pages/
    <db-master-name>/               # ONE per UI Maker page
      <name>.component.ts/.html/.scss   # page component: grid + form wiring, page jsCode hooks, page cssCode
      <name>.main.ts                # <= userUtils.Main.* (page-specific JS functions)
      <name>.api.ts                 # page-specific endpoints / overrides
      <name>.model.ts               # typed row + form model interfaces
  styles.scss                       # global: Bootstrap grid+utilities + shared cssCode (common CSS)

Recommended approach β€” per-page hand-written components backed by reusable building blocks. Build a small library of reusable, typed primitives (a generic <um-grid>, the 24 control components, the shared services), then hand-write one explicit component per UI Maker page that composes them with the page's exact behavior. Rationale: a fully generic runtime engine would just re-implement UI Maker; explicit per-page components are debuggable, type-safe, tree-shakeable, and let you delete dead config. Reuse the primitives so you do not re-implement PrimeNG wiring 24 times.

Key mappings:

  • Reactive forms: each form is a FormGroup. Dot-notation path (a.b.c) maps to nested FormGroups (form.get('a.b.c')). Provide form-path.util.ts to build the nested group from all field paths and to get/set by path.
  • 2-D fields[][] layout: outer array = a <div class="row">; inner array = the columns inside that row. Each field wrapper gets cssClassDiv classes (default col-lg mt-4 col-md-{calculated based on columns.length} β€” compute the col-md-* span from the number of columns in that inner array). Bootstrap 5.3.3 grid is the layout system.
  • API layer replaces API Maker auto-generated calls and apiCallOverrides (see Β§9).
  • Parent integration (parent-integration.service.ts) reproduces the iframe postMessage contract (see Β§12). If the converted app is no longer embedded as an iframe, replace inbound parent events with component @Input()s / route/query params and outbound events with @Output() EventEmitters β€” but preserve the same semantics and payloads.

4. Global vs Page-specific CSS & JS (READ CAREFULLY β€” explicit requirement)

UI Maker has two tiers of custom CSS and JS: common/shared (used across many or all pages) and page-specific. You must split them correctly.

4.1 CSS

Source (UI Maker) What it is Target (Angular)
Common/global CSS used across many pages shared style overrides Put into the global styles.scss (or a shared _um-shared.scss imported there). Loaded once, applies everywhere.
Page-specific CSS: config.cssCode: [{ runOn: 'header', code: '<raw CSS>' }] raw CSS injected into <head> for that page Put into that page component's stylesheet (<name>.scss). Note runOn is always 'header'. If a class must override already-applied global/theme classes (UI Maker allows overriding existing classes), keep the same selectors and use :host ::ng-deep or move it to global scope as needed to win specificity.
Inbuilt Bootstrap 5.3.3 grid + utilities col-*, mt-*, row, etc. used by cssClassDiv Ensure Bootstrap grid + utilities CSS is loaded globally so cssClassDiv classes keep working.

Mapping example:

// BEFORE (UI Maker page config)
cssCode: [{ runOn: 'header', code: `.font-orange { color: orange; }` }]
/* AFTER β€” <name>.scss (page-specific) */
.font-orange { color: orange; }

4.2 JS

UI Maker JS comes in three forms for the code value: an inline JS string, a function ($scope) => any, or a reference to a named external function (userUtils.Main.fnName or userUtils.GeneralUtils.fnName). Code may mutate the provided data in place, return a Promise (await it), or return a function (invoke it).

Source (UI Maker) What it is Target (Angular)
Common/global JS: userUtils.GeneralUtils.<fn> shared utilities callable from any page via code: 'userUtils.GeneralUtils.fn' Implement as methods/exports in ui-maker-shared/utils/general-utils.ts, imported by all pages.
Page-specific JS (named): userUtils.Main.<fn> per-page functions (e.g. userUtils.Main.first_name_blur) referenced from config Implement as methods on that page's <name>.main.ts (or the page component).
Page-level lifecycle hooks: top-level config.jsCode: [{ appendTo: EDBMasterConfigAppendTo, code }] run at named page lifecycle points Implement as page component methods/lifecycle hooks (see table below).
Per-control hooks: <controlSettings>.jsCode: [{ appendTo, code }] run at named control events Implement as the control component's event handlers (see Β§7).

Top-level page lifecycle hooks β€” EDBMasterConfigAppendTo β€” and their available variables. Translate each to the natural Angular equivalent:

appendTo Available variables in scope Angular equivalent
oncePageLoad (NONE β€” no config, no globalData) run once early in ngOnInit before context is ready
oncePageLoadWithContext config, globalData, utils, queryParams run once in ngOnInit after config/parent context resolved
gridRender gridEvent, gridData, globalData, utils, queryParams hook that runs every time grid data loads; may mutate gridData rows; may return a Promise (await before render)
modifyGridRequest gridEvent, reqBody (IQueryFormat), globalData, utils, queryParams; also moment, _ available mutate the find/sort/select request body before the grid data fetch
onceGridDataLoaded (grid data context) run after grid data first loads
preSaveAndEdit reqBody, globalData, utils, queryParams, mode ('save' \| 'edit' \| 'import') run before save/update/import; branch on mode; may delete fields from reqBody
beforeSaveModalOpen formData, utils (form-introspection) mutate formData before the Add dialog opens (e.g. formData.remarkDate = new Date())
beforeEditModalOpen formData, utils mutate formData before the Edit dialog opens
beforeViewModalOpen formData, utils mutate formData before the View dialog opens
columnSelectionChanged selected columns react to column-selector change
deleteConfirmationDialogGenerator / deleteAllConfirmationDialogGenerator / editConfirmationDialogGenerator / saveConfirmationDialogGenerator the relevant item(s) produce the confirmation message/dialog content
processDataBeforeExport export rows transform rows before CSV export
processDataBeforePDFExport export rows + jsPdfDoc (jsPDF) (PDF export is partially stubbed; CSV is the supported path)

utils helpers exposed to hooks/scripts β€” re-implement on a utils object passed into every hook:

  • utils.findFormElement(pathOrHiddenId) β€” returns the field config by its path or its hiddenId.
  • utils.getFlatArrayOfAllFormFields() β€” flat array of all form fields excluding grid fields.
  • utils.getFlatArrayOfAllFormFieldsIncludingGridFields() β€” flat array including nested grid fields.
  • utils.messageService.showInfoToast(msg, opts) / showSuccessToast(msg, {life}) / showErrorToast(...) β€” toast notifications.
  • utils.errorsMap[path] = 'message' β€” set a validation error on another field by path (reflect onto that reactive control's errors).
  • window.top.postMessage({ eventType, eventData }, '*') β€” send a custom event to the parent (see Β§12).

globalData is the object pushed by the parent via SET_GLOBAL_DATA_TO_USE_IN_ANY_SCRIPT; pass it into every hook scope. queryParams are the URL query params.


5. Top-level grid configuration β†’ PrimeNG p-table

Build a reusable <um-grid> around p-table. Map every key below. Defaults are stated; apply them when the key is absent.

5.1 Grid-level keys

UI Maker key Type / default PrimeNG / Angular equivalent Notes
grid.header string grid title/caption header
grid.minWidth default '50rem' [style]="{'min-width': minWidth}" on <p-table>
grid.breakpoint default '40rem' [breakpoint] (responsive)
grid.currentPageReportTemplate e.g. 'Showing {first} to {last} of {totalRecords} entries' [currentPageReportTemplate] + paginator report
grid.showColumnSelector default false p-multiSelect of columns toggling visible columns
grid.dataFieldPath string nested grid only: pick the data array from this field path not for top-level grid
grid.selectFieldsForQuery projection {field:1\|-1} (default all DB fields) API Maker query select; pass into the data-fetch request not for nested grids
grid.sortMode 'single'\|'multiple', default 'single' [sortMode] 'multiple' requires the meta key when adding columns
grid.multiSortMeta [{field, order:1\|-1}] [multiSortMeta] each field must have enableSorting

5.2 Columns β€” grid.fields[] (IDBMasterConfigGridField)

If grid.fields is empty/omitted: auto-generate columns from the schema in schema order, EXCLUDING the Mongo _id column.

Per-column key Equivalent Notes
header <th> text
path cell value path; supports dotted (country.name) resolve nested
enableSorting pSortableColumn="path" + <p-sortIcon>
visibilityStatus EDBMasterConfigGridColumnVisibilityStatus.INVISIBLE \| VISIBLE initial column visibility (used with column selector)
autoFormatDate format the cell as a date default format dd-MM-yyyy hh:mm:ss a (date-fns); see Β§13
dateTimeFormat per-column date format override date-fns
cssClass CSS class on the cell/column
dbData (FK lookup) { collection, pkField, displayField } resolve the stored key (e.g. _id) to a display value via a lookup; in nested-grid columns use dataSource:'db_data' + dbData:{pkField, displayField}
dataSource 'db_data' in nested-grid columns resolve FK display

5.3 Pagination β€” grid.pagination

Key Default Equivalent
showPagination false [paginator]
serverSidePagination false [lazy]="true" + (onLazyLoad) server fetch; NOT supported for nested grids
rowsPerPage 10 [rows]
rowsPerPageOptions [10,20,50,100,500,1000] [rowsPerPageOptions]

5.4 Selection β€” grid.selection

Key Default Equivalent
mode 'multiple' single β†’ selectionMode="single"; multiple β†’ "multiple"
dataKey auto from schema (PK) [dataKey]
metaKeySelection true [metaKeySelection] (true = needs Ctrl/Cmd)
checkboxSelection false (multiple only) <p-tableHeaderCheckbox> + <p-tableCheckbox> column

5.5 Filter / search β€” grid.filter

Key Default Equivalent
showGlobalSearch false global filter input
globalSearchFields string[] (dotted paths ok) string β†’ contains, number β†’ exact match; pass to [globalFilterFields] and/or server query
rowFilter false per-column <p-columnFilter>

5.6 Grouping β€” grid.grouping (rowGroupMode 'subheader' only)

groupRowsBy β†’ [rowGroupMode]="'subheader'" + [groupRowsBy]; headerStyle/headerCssClass (group header <tr>); footerStyle/footerCssClass; footerColumnPath (field used to render the group footer). rowspan grouping is not supported.

5.7 Row operations β€” grid.operations

Key Default Equivalent / behavior
delete { enable, cssClass, apiCallOverrides, apiCallOverridesDeleteMany } row delete button (icon styled by cssClass, e.g. text-danger)
edit { enable, cssClass, apiCallOverrides, apiCallOverridesGetById } row edit button (e.g. text-info); edit fetches single record via apiCallOverridesGetById
hideDeleteConfirmation / hideEditConfirmation suppress per-row confirm dialog
hideDeleteManySuccessMessage false suppress bulk-delete success toast (then you must show it manually)
disableDoubleClickForEdit (default: double-click row opens edit) when set, disable double-click-to-edit
disableDeleteButtonClickForDelete (default: delete button deletes) when set, delete button does not delete
gridOperationsOrder EDBMasterGridOperationsOrder[] = delete \| edit \| custom (only these 3) order of the row action buttons
gridColumnsOrder EDBMasterGridColumnsOrder[] = checkbox \| dbFields \| actions (only these 3) order of column groups
actionColumnWidth e.g. '120px' width of the actions column
customActionButtons[] { actionName, cssClass (icon e.g. 'pi pi-check'), style (e.g. {color:'yellow'}), confirmationMessageScript } per-row custom button. confirmationMessageScript is a JS template-literal string evaluated with selectedGridItems in scope, e.g. "`Do you want to approve ${selectedGridItems.name}?`"

Per-row action hiding (magic flags on the ROW DATA object) β€” when rendering each row's buttons, honor: ____hide_delete_button, ____hide_edit_button, ____hide_view_button, and ____hide_<actionName> (custom) β€” if true on the row object, hide that button for that row only.

5.8 Toolbar operations β€” top-level operations

Key Default Equivalent
operations.add IDBMasterConfigOperation { enable, label, apiCallOverrides } Add button β†’ opens add form dialog; title 'Add ${screenName}'
operations.hideAddConfirmation suppress add confirm
operations.hideDeleteAllButton false hide Delete-All button
operations.hideDeleteAllConfirmation suppress delete-all confirm
operations.showRefreshButton Refresh button (reload grid)
operations.exportData see Β§10 Export button
operations.importData see Β§10 Import button
operations.customActionButtons[] IDBMasterTopLevelCustomActionButton[] toolbar custom buttons (distinct from row ones)
operations.operationsOrder EDBMasterOperationsOrder[] order toolbar items; default order: theme_selector, column_selector, global_search, import, export, refresh, add, custom_actions, delete_all

5.9 Grid data fetch override

grid.apiCallOverrides (IDBMasterAPICallOverrides) overrides the get-grid-data API (see Β§9). Note: in the grid (non-form) context, formData and allDropdownDataMap are not available to the override hooks.

IDBMasterConfigGridField, IDBMasterConfigOperation, IDBMasterCustomActionButton, and IDBMasterTopLevelCustomActionButton are not given standalone interface blocks in the docs β€” infer their exact shapes from the page's actual config and the worked examples here.


6. Form framework β†’ Angular Reactive Forms

6.1 Layout

form.fields is a 2-D array (outer = rows, inner = columns). A field that itself has a nested fields (also 2-D) and no control is a pure layout container β€” render its children but bind no value for it. form.width sets the dialog (p-dialog) width. The form runs in three modes: add, edit, view (view = read-only).

6.2 Shared base field props (IDBMasterConfigFormField) β€” present on EVERY control

Prop Type / default Handling
hiddenId string element id for programmatic access (utils.findFormElement)
label string field label (may be ' ' to reserve space for alignment)
helpText HTML string render below the control via [innerHTML] (sanitized)
path dotted string the nested FormControl key where the value is stored
control EDBMasterFormControl which control component to render
cssClassDiv default col-lg mt-4 col-md-{calc by columns.length} wrapper div classes
autofocus default false focus on form open
disabled boolean \| JS-expression string static OR evaluated expression (see Β§6.3)
visible boolean \| JS-expression string, default true static OR evaluated expression
fields IDBMasterConfigFormField[][] nested 2-D layout
validations Pick<IPropertyValidation,'required'> & { requiredFun?: string } required (boolean) + requiredFun (dynamic expression)
validationErrors.required string custom required error message

Build the FormGroup from all field paths (nested groups for dotted paths). Add/remove Validators.required per validations.required. Set [autofocus]. Render helpText HTML below each control. Container fields and value-less controls (button, image, customHTML, divider, accordion, tab_view) must NOT be registered as value-bearing FormControls unless they actually carry a path.

6.3 JS-expression-string evaluation (visible / disabled / requiredFun)

visible, disabled, and requiredFun may each be a boolean OR a JavaScript expression string evaluated against form data, e.g. "formData.type === 'readonly'", "formData.userRole === 'admin'", "formData.type === 'individual' ? true : false".

  • Implement a single expression.service.ts evaluator. Build the expression scope: { formData, queryParams, globalData, utils, column, config }.
  • Recommended safe approach: a constrained new Function('formData','queryParams','globalData','utils','column','config', 'return (' + expr + ');') invoked with those args, wrapped in try/catch. Do not use eval. (If your security posture forbids new Function, swap in a sandbox like a small expression interpreter, but the string contract must be honored.)
  • Re-evaluate reactively on form.valueChanges (and on globalData changes). Bind visible β†’ *ngIf / [hidden]; disabled β†’ control.disable()/enable() (and [disabled] for non-control elements).
  • requiredFun takes precedence over static required when both are present: when requiredFun exists, ignore static required and dynamically add/remove Validators.required based on the evaluated result on every value change.
  • The full IPropertyValidation set exists at schema level (required, min, max, minLength, maxLength, unique [deprecated], email, validatorFun, enum). Most controls only expose required/requiredFun via validations, but control-specific validators appear in settings (e.g. inputTextSettings.minLength, inputNumberSettings.min/max/minLength, editorSettings.maxLength). Map each to the matching Angular Validators.*; map a custom validatorFun to a custom validator; map enum to a membership validator.

7. Per-control conversion reference (THE BIG SECTION β€” all 24 controls)

For every control: render the listed PrimeNG component bound to a FormControl at path (support dotted paths); honor the shared base props (Β§6.2); apply style as [ngStyle], cssClass as styleClass, tooltip/tooltipPosition/tooltipStyleClass via pTooltip; wire the control's jsCode hooks to the matching PrimeNG outputs. The jsCode $scope (IDBMasterUIPageUtilsScope) for input-like controls is { formData, column, allDropdownDataMap, globalData, utils, queryParams, config, event } (data-source controls additionally get reqBody, dropdownData, reloadDropdownsOfPath). Code may be a string, a function, or a userUtils.* reference, may mutate data, return a Promise (await), or return a function (invoke).

7.1 input β€” p-inputText (pInputText on <input>)

  • Settings (inputTextSettings): style, placeholder, autocomplete (default off), spellcheck (default false), maxLength (β†’ maxlength attr + Validators.maxLength), minLength (β†’ Validators.minLength), tooltip/tooltipPosition/tooltipStyleClass, jsCode, validationErrors.minLength (custom minLength message).
  • Validation: required/requiredFun; minLength (custom msg via inputTextSettings.validationErrors.minLength); maxLength.
  • Events (EDBMasterInputTextAppendTo): visible, disabled, ngModelChange, focus, blur, keyUp, keyDown.

7.2 textarea β€” pInputTextarea on <textarea>

  • Settings (textAreaSettings): style, placeholder, rows, autoResize (default false), maxLength, tooltip*, jsCode.
  • Validation: required/requiredFun; maxLength. (No minLength/email/pattern exposed.)
  • Events (EDBMasterTextAreaAppendTo): visible, disabled, ngModelChange, focus, blur, keyUp, keyDown.

7.3 password β€” p-password

  • Settings (inputPasswordSettings): style, cssClass, placeholder, maxLength, tooltip*, jsCode. (toggleMask, feedback, prompt/strength labels referenced in examples β€” map to p-password [toggleMask]/[feedback]/labels.)
  • Validation: required/requiredFun; maxLength.
  • Events (EDBMasterInputPasswordAppendTo): visible, disabled, ngModelChange, focus, blur, keyUp, keyDown.

7.4 inputNumber β€” p-inputNumber

  • Settings (inputNumberSettings): style, cssClass, placeholder, min, max, minFractionDigits, mode ('decimal'\|'currency'\|''), currency (ISO 4217 β€” required when mode='currency', no default), locale (default 'en-US'), prefix, suffix, showButtons, buttonLayout (default 'stacked'; also horizontal/vertical), step, useGrouping, maxLength, minLength, tooltip*, jsCode, validationErrors.minLength.
  • Validation: required/requiredFun; min/max (PrimeNG constraint); minLength/maxLength; minFractionDigits.
  • Events (EDBMasterInputNumberAppendTo): visible, disabled, ngModelChange, focus, blur, keyUp, keyDown.

7.5 inputMask β€” p-inputMask

  • Settings (inputMaskSettings): style, cssClass, mask (a=alpha, 9=numeric, *=alphanumeric, ?=optional remainder, literals ( ) - / . , :; e.g. '(999) 999-9999', '99/99/9999', '99:99 aa'), placeholder, slotChar (default _), autoClear (default true β€” clears incomplete value on blur), maxLength, tooltip*, jsCode.
  • Validation: required/requiredFun; maxLength; mask enforces format/length; autoClear enforces completeness.
  • Events (EDBMasterInputMaskAppendTo): visible, disabled, ngModelChange, focus, blur, complete, keyUp, keyDown.
  • GOTCHA: modifying the model value inside the blur hook does NOT work (PrimeNG bug) β€” route model-mutating logic to the complete hook.

7.6 inputOtp β€” p-inputOtp

  • Settings (inputOtpSettings): mask (boolean β€” obscure values, password-style), integerOnly (numeric only), uiControlWidth (default 300px), length (number of boxes / OTP length), tooltip*.
  • Validation: required/requiredFun; integerOnly; length (fixed OTP length).
  • Events: none control-specific; only evaluation-based visible/disabled/requiredFun. Auto-focus between boxes is built-in.

7.7 date_picker β€” p-calendar (Calendar / DatePicker)

  • Value is always a native JS Date stored at path (write back a Date, not ISO string).
  • Settings (datePickerSettings): style, cssClass, placeholder, showSeconds, showTime, hourFormat ('12'\|'24', default '24'), dateTimeFormat, timeOnly, minDate (Date), maxDate (Date), view ('date'\|'month'\|'year', default 'date'), tooltip*, jsCode.
  • Mapping: showTimeβ†’[showTime], showSecondsβ†’[showSeconds], hourFormatβ†’[hourFormat], timeOnlyβ†’[timeOnly], minDate/maxDateβ†’[minDate]/[maxDate], viewβ†’[view], dateTimeFormatβ†’[dateFormat] (convert date-fns tokens to PrimeNG calendar tokens, e.g. dd-MM-yyyy β†’ dd-mm-yy; see Β§13).
  • GOTCHA: for view='month' set dateTimeFormat='MM-yyyy'; for view='year' set 'yyyy'. minDate/maxDate are selection constraints only β€” add equivalent validators if true validation is needed.
  • Validation: required/requiredFun.
  • Events (EDBMasterDatePickerAppendTo): visible, disabled, ngModelChange (on onSelect/value change), focus, blur.

7.8 checkbox β€” p-checkbox (binary)

  • Bind a boolean FormControl; [binary]="true".
  • Settings (checkboxSettings): tooltip*, jsCode (onChange).
  • Validation: required/requiredFun.
  • Events (EDBMasterCheckboxAppendTo): ngModelChange (a.k.a. onChange β†’ (onChange)), visible, disabled.

7.9 radio β€” p-radioButton (one per option, single FormControl)

  • Settings (radioSettings): displayType ('inline' = horizontal one line, 'new_line' = vertical), displayInCenter (center the group), options[] (REQUIRED): { label, value } (value any type: string/boolean/number β€” selected option's value is stored), tooltip*, jsCode.
  • Validation: required/requiredFun.
  • Events (EDBMasterRadioAppendTo): ngModelChange, visible, disabled.

7.10 dropdown β€” p-dropdown (data-source control β€” see Β§8)

  • Settings (dropdownSettings): style, cssClass, placeholder, showClear (default false), dataSource (REQUIRED: 'static_data'\|'db_data'\|'api_call'), staticData[], dbData (Partial<{instance,database,collection,table}> & {find,select,limit,deep,sort} β€” select may be CSV string OR object map {field:1}; identity may be inherited from schema), optionLabel (default 'label', HTML supported β†’ use item template with [innerHTML]), optionValue (default 'value', stored value), filter (default false), filterBy (comma-separated, no spaces), filterMatchMode (default 'contains'; also startsWith\|endsWith\|equals\|notEquals\|in\|lt\|lte\|gt\|gte), alwaysGetLatestDataOnFormOpen (default false), virtualScroll (default false), reloadDropdownsOfPath[], isDependentOnPath[], tooltip*, jsCode, addNewFormConfig (IDBMasterConfig β€” inline add modal), apiCallOverrides.
  • Validation: required/requiredFun.
  • Events (EDBMasterDropdownAppendTo): visible, disabled, modifyDropdownRequest (mutate reqBody before fetch), onceDropdownDataLoaded (post-process dropdownData), onChange.

7.11 auto_complete β€” p-autoComplete (data-source control β€” see Β§8) (v1.11+)

  • Stores the selected option's optionLabel value (there is NO optionValue β€” it is commented out; the whole label-field value is stored).
  • Settings (autocompleteSettings): style, cssClass, placeholder, showClear, minLengthForSearch (β†’ minLength), delay (debounce ms), forceSelection (clears manual input not matching a suggestion), dataSource, staticData[], dbData, optionLabel (default 'label', HTML supported), filterBy, filterMatchMode (contains\|startsWith\|endsWith only), alwaysGetLatestDataOnFormOpen, virtualScroll, reloadDropdownsOfPath[], isDependentOnPath[], dropdown (show dropdown button), maxLength, tooltip*, addNewFormConfig, apiCallOverrides, jsCode.
  • completeMethod: fire only after minLengthForSearch chars, debounced by delay; for db_data/api_call honor isDependentOnPath (skip until all parent paths have values).
  • Validation: required/requiredFun; forceSelection; maxLength.
  • Events (EDBMasterAutoCompleteAppendTo): onSelect, visible, disabled, modifyAutoCompleteRequest, onceAutoCompleteDataLoaded, focus, blur, keyUp, keyDown, onShow, onHide, onClear, onDropdownClick.

7.12 multi_select β€” p-multiSelect (data-source control β€” see Β§8)

  • Settings (multiselectSettings): style, cssClass, placeholder, showClear, minLength, delay, dataSource, staticData[], dbData, optionLabel (default 'label', HTML), optionValue (default 'value'), showHeader, filter, filterBy, filterMatchMode (default 'contains'; full set), alwaysGetLatestDataOnFormOpen, virtualScroll, maxSelectedLabels, selectionLimit, selectedItemsLabel (e.g. '{0} items selected'), showToggleAll, optionDisabled (field marking an option disabled), optionGroupLabel + optionGroupChildren (grouping), display ('comma'\|'chip'), reloadDropdownsOfPath[], isDependentOnPath[], addNewFormConfig, apiCallOverrides, tooltip*, jsCode.
  • Validation: required/requiredFun; selectionLimit.
  • Events (EDBMasterMultiSelectAppendTo): visible, disabled, modifyMultiSelectRequest, onceMultiSelectDataLoaded, onChange (+ panel show/hide/clear as applicable).

7.13 color_picker β€” p-colorPicker

  • Settings (colorPickerSettings): style, cssClass, format ('hex'\|'rgb'\|'hsb'), tooltip*, jsCode.
  • Validation: required/requiredFun.
  • Events (EDBMasterColorPickerAppendTo): visible, disabled, ngModelChange.

7.14 editor β€” p-editor (Quill WYSIWYG) (v1.14.0+)

  • Value is HTML rich text; round-trip and sanitize on render.
  • Settings (editorSettings): style (e.g. {height:'350px'}), placeholder, maxLength (no native maxlength β€” enforce via Validators.maxLength and/or guard in onTextChange), tooltip*, formats[] (whitelist of allowed Quill formats from: background, bold, color, font, code, italic, link, size, strike, script, underline, blockquote, header, indent, list, align, direction, code-block, image, video, clean; configure the Quill toolbar/formats whitelist β€” when omitted use full default toolbar), jsCode.
  • Validation: required/requiredFun; maxLength.
  • Events (EDBMasterEditorAppendTo): visible, disabled, ngModelChange, onTextChange, onInit, onSelectionChange, focus, blur, keyUp, keyDown.

7.15 rating β€” p-rating

  • Settings (ratingSettings): cancel (default true β€” show clear icon), stars (number of stars = max value), iconOnClass/iconOnStyle, iconOffClass/iconOffStyle, iconCancelClass/iconCancelStyle, onIconHTML/offIconHTML/cancelIconHTML (raw custom HTML icons, e.g. <img> or <i> β€” override class/style), tooltip*, jsCode.
  • Validation: required/requiredFun.
  • Events (EDBMasterRatingAppendTo): ngModelChange, onRate, onCancel, onFocus, onBlur, visible, disabled.

7.16 knob β€” p-knob

  • Settings (knobSettings): min, max, step, size (px diameter, e.g. 150, 200), strokeWidth, valueColor, rangeColor, textColor, valueTemplate (e.g. '{value}%', '{value}Β°C' β€” {value} substituted), cssClass, style, tooltip*, jsCode.
  • Validation: required/requiredFun; min/max/step enforced by knob.
  • Events (EDBMasterKnobAppendTo): ngModelChange, visible, disabled.

7.17 file_upload β€” p-fileUpload (full spec)

  • Stored value at path = array of { originalName: string, uploadPath: string } objects (extra props allowed). NOT raw File objects.
  • Settings (fileUploadSettings): style, cssClass, auto (default false; true auto-uploads after selection, else show upload button), uploadApiUrl (REQUIRED), downloadApiUrl, removeApiUrl, multiple, accept (e.g. 'image/*', '.pdf,.doc,.docx'), maxFileSize (default 10000000 = 10MB), fileLimit, tooltip*. (_showUploadButton, _fileSelectEvent are internal β€” do not expose; you may need equivalent internal state.)
  • Contracts (resolve :beHostPort / :userPath placeholders first β€” see Β§9):
  • UPLOAD: POST files in multipart field named exactly files to uploadApiUrl; response is [{originalName, uploadPath}, ...] β†’ becomes the FormControl value. Use p-fileUpload customUpload/uploadHandler.
  • DOWNLOAD/PREVIEW: call downloadApiUrl β†’ returns base64; use for inline preview (data URI for images) and download.
  • REMOVE: call removeApiUrl with a single { originalName, uploadPath } payload, then update the FormControl array.
  • Validation: required/requiredFun; maxFileSize; fileLimit; accept.
  • No data-source / cascading behavior.

7.18 image β€” p-image (display-only; <img> inside <span>)

  • Settings (imageSettings): src (REQUIRED; may start empty and be set dynamically via jsCode from formData, e.g. column.imageSettings.src = formData.profilePicture), style, cssClass, imageParentSpanClass (class on the wrapping <span>), width, height, alt, preview (fullscreen preview on click), previewImageSrc (separate high-res preview source, distinct from thumbnail src), tooltip*, jsCode.
  • Display-only β€” do NOT register as a value FormControl unless a path is set. Often paired with file_upload to preview uploaded images. Re-render when src changes.
  • Events (EDBMasterImageAppendTo): visible, disabled, click.

7.19 button β€” p-button (action only, no value)

  • Render as a standalone <p-button>; do NOT add it as a value-bearing FormControl.
  • Settings (buttonSettings): label (required, can be empty for icon-only), style, cssClass, link, icon, iconPos (default 'left'; right/top/bottom), loading, loadingIcon, severity (primary/success/info/warning/danger), raised, rounded, text, outlined, badge, badgeClass (newer PrimeNG: badgeSeverity), size ('small'\|'large'), tooltip*, jsCode (click).
  • Events (EDBMasterButtonAppendTo): click (β†’ (onClick)), visible, disabled. Click code commonly uses utils.messageService.showSuccessToast(...) and utils.errorsMap[path]='message'.

7.20 divider β€” p-divider (presentational, no value)

  • Settings (dividerSettings): style, cssClass, align (center/left/right for horizontal; top/center/bottom for vertical β†’ [align]), type (dashed/dotted/solid β†’ [type]), dividerText (projected content; or use field label).
  • No control-specific events; only visible/disabled (evaluated).

7.21 customHTML β€” <div [innerHTML]> + DomSanitizer (no PrimeNG component)

  • Settings (customHTMLSettings): htmlCode (REQUIRED) β€” raw HTML rendered via [innerHTML]. Sanitized (XSS protection β€” some tags/attributes/event-handlers/scripts stripped). Use Angular DomSanitizer.sanitize (NOT bypassSecurityTrust) to preserve the same protective behavior. Keep inline style attributes intact where possible.
  • No value, no FormControl. Primary dynamic feature is visible.

7.22 accordion β€” p-accordion (field container, no value)

  • Settings (accordionSettings): style, cssClass, activeIndex (number | number[]), defaultIndex (default 0; seed activeIndex if absent), multiple (β†’ [multiple]; in multiple mode activeIndex is number[]), jsCode, tabs[]: each { style, header (HTML β†’ render with[innerHTML]), headerStyle, headerCssClass, disabled (**boolean OR JS-expression string**), fields (2-D nested fields) }.
  • Recursively render each tab's fields[][] as their own reactive controls; the accordion contributes no value of its own.
  • Events (EDBMasterAccordionAppendTo): visible, disabled, activeIndexChange.

7.23 tab_view β€” p-tabView + p-tabPanel (field container, no value)

  • Settings (tabViewSettings): activeIndex, defaultIndex (default 0), style, cssClass, jsCode, tabs[]: { style, header (HTML), headerStyle, headerCssClass, disabled (**boolean only** here), fields (2-D nested fields) }.
  • Events (EDBMasterTabViewAppendTo): visible, disabled, activeIndexChange (β†’ (onChange)/(activeIndexChange)).

7.24 grid (nested) β€” p-table + add/edit p-dialog backed by a FormArray of FormGroups

  • Value is an array stored at path β†’ model as a FormArray<FormGroup>; bind to <p-table> [value]; edit/add via a p-dialog hosting the nested form.
  • Settings (gridSettings) = Omit<IDBMasterConfig,'schema'> β€” a full child config (its own operations, grid, form, jsCode, externalLibs, cssCode, apiCallOverrides...). Recurse: a nested grid inside gridSettings.form is another FormArray + p-table + dialog. Unlimited nesting. For nested grids, locate the child array via dataFieldPath; serverSidePagination and selectFieldsForQuery are NOT supported for nested grids.
  • Reproduce all grid features (Β§5), row magic-hide flags, the Add modal flow (operations.add.enable/label; on save push the FormGroup value into the FormArray; respect hideAddConfirmation), edit (open dialog with row), delete (remove from array; respect confirmations/disableDoubleClickForEdit/disableDeleteButtonClickForDelete), db_data column FK resolution (collection/pkField/displayField β†’ display value), and nested-form dropdown loading (re-fetch on dialog open when alwaysGetLatestDataOnFormOpen).
  • Lifecycle hooks (gridSettings.jsCode): oncePageLoad, oncePageLoadWithContext, gridRender, modifyGridRequest, preSaveAndEdit (mode), beforeSaveModalOpen, beforeEditModalOpen (see Β§4.2).
  • Nested form fields carry the full IPropertyValidation (required, min, max, minLength, maxLength, email, validatorFun, enum) β€” convert each to Angular validators.

8. Dependent / cascading dropdowns

Cascading is NOT automatic. It requires, on the relevant dropdownSettings/autocompleteSettings/multiselectSettings, all three of:

  1. reloadDropdownsOfPath: string[] β€” on the parent control. When the parent's value changes, the child controls at these paths are cleared and reloaded.
  2. isDependentOnPath: string[] β€” on the child control. The child loads data only when all listed parent paths have values in formData.
  3. jsCode modify{Dropdown|AutoComplete|MultiSelect}Request β€” on the child. Mutates reqBody to inject the parent value into the query, e.g. reqBody.find.continentId = formData.continentId;.

N-level example: planet β†’ continent β†’ country β†’ state

// continent depends on planet, reloads country on change
{ path:'continentId', control:'dropdown', dropdownSettings:{
  dataSource:'db_data', dbData:{ collection:'ui_maker_continent' }, optionValue:'_id', optionLabel:'name',
  isDependentOnPath:['planetId'], reloadDropdownsOfPath:['countryId'],
  jsCode:[{ appendTo:'modifyDropdownRequest', code:`reqBody.find.planetId = formData.planetId;` }]
}}

Angular re-implementation:

  • For each control with reloadDropdownsOfPath, subscribe to its FormControl valueChanges. On change: for each child path, reset the child's value and trigger its reload (clear cached options and re-fetch).
  • For each control with isDependentOnPath, gate loadOptions() behind a check that every listed parent path has a non-empty value in formData; otherwise leave options empty/disabled.
  • Before each child fetch, run the modify*Request hook against a mutable reqBody object so it can inject parent values (reqBody.find.<parent> = formData.<parent>), then send that body to the data service.
  • Run once*DataLoaded after the fetch to post-process dropdownData.
  • Cascading mixes freely across dropdown / auto_complete / multi_select. The chain can be arbitrarily deep β€” reset and reload transitively down the chain. reloadDropdownsOfPath is also available as a mutable variable inside hooks: pushing a path into it triggers that path's reload.

9. API layer & overrides

Replace API Maker's auto-generated calls and all apiCallOverrides with an explicit api.service.ts (HttpClient).

IDBMasterAPICallOverrides:

interface IDBMasterAPICallOverrides {
  url: string;                 // REQUIRED; supports :beHostPort and :userPath tokens
  headers?: any;               // custom request headers
  method?: 'POST';             // ONLY 'POST' is supported
  body?: any;                  // custom request body
  pkField?: string;            // PK field name; '_id' for MongoDB (delete/getById/deleteMany)
  codeBeforeAPICall?: string;  // JS hook: mutate request before call
  codeAfterAPICall?: string;   // JS hook: mutate response after call
}
  • All override calls are POST. Implement only POST.
  • Placeholder substitution (do before every call): :beHostPort β†’ backend protocol+host+port (e.g. https://example.com); :userPath β†’ the admin user path segment.
  • codeBeforeAPICall / codeAfterAPICall scope: config, formData, allDropdownDataMap, apiResponse (after only), globalData, headers, reqBody, utils, queryParams, mode. Note: in the grid context, formData and allDropdownDataMap are NOT available.

Per-operation overrides to implement (each points to a custom endpoint):

Override path Operation Extra
operations.add.apiCallOverrides create/save
operations.importData.apiCallOverrides CSV import persist default uses save API
grid.apiCallOverrides get grid data
grid.operations.delete.apiCallOverrides delete by id uses pkField
grid.operations.delete.apiCallOverridesDeleteMany bulk delete uses pkField
grid.operations.edit.apiCallOverrides edit/update by id
grid.operations.edit.apiCallOverridesGetById fetch single record before edit uses pkField
dropdownSettings/autocompleteSettings/multiselectSettings.apiCallOverrides dropdown options (dataSource:'api_call') map response via optionLabel/optionValue

When no override exists, call the standard API Maker endpoint for that operation; build query bodies (find, select, limit, deep, sort) via query.service.ts using the API Maker IQueryFormat shape (selectFieldsForQuery projection {field:1|-1}).


10. CSV import & export

10.1 Import β€” operations.importData (IDBMasterImportData)

interface IDBMasterImportData {
  enable: boolean;
  icon?: string;                 // default 'pi pi-file-import'
  gridWidth?: string;            // import preview grid width, e.g. '2000px'
  fieldMappings: IDBMasterImportGridColumn[];
  apiCallOverrides?: IDBMasterAPICallOverrides; // default: the save API
}
interface IDBMasterImportGridColumn {
  header: string;                          // UI preview grid header
  sourceField: string;                     // CSV column name (e.g. 'NAME', 'DOB')
  targetField: string;                     // DB field path (e.g. 'name', 'dateOfBirth')
  convertTargetFieldToType: EType;         // convert CSV value to this type before save (e.g. string, date)
  dateParseFormat?: string;                // date-fns parse format (default ISO)
  dateTimeZone?: string;                   // tz to convert parsed date into before DB query
  dateFormatUIDisplay?: string;            // date format for UI display in preview
  dateTimeZoneUIDisplay?: string;          // tz for UI display in preview
  width?: string;                          // per-column preview width (e.g. '300px')
  filterType?: EDBMasterConfigFilterType;  // CALCULATED/internal β€” do not set
}

Implement: a file picker → parse CSV with Papa Parse (available globally as Papa) → render an editable preview p-table (columns from fieldMappings, widths from width/gridWidth) → for each row map sourceField→targetField, convert to convertTargetFieldToType (use date-fns parse with dateParseFormat; convert tz via date-fns-tz with dateTimeZone; display in preview using dateFormatUIDisplay/dateTimeZoneUIDisplay) → POST to the save API (or apiCallOverrides).

10.2 Export β€” operations.exportData

operations: { exportData: { showExportButton: true, icon: 'pi pi-download',
  csv: { enable: true, labelCSV: 'CSV', labelCSVSelected: 'CSV Selected' } } }
Key Default Behavior
showExportButton show Export button
icon 'pi pi-download' button icon
csv.enable enable CSV export
csv.labelCSV 'CSV' "export all" label
csv.labelCSVSelected 'CSV Selected' "export selected rows" label

Provide export all and export selected actions. Run the processDataBeforeExport hook to transform rows first. (PDF export is partially stubbed via processDataBeforePDFExport + jsPdfDoc β€” CSV is the fully supported path.)


11. Theming

  • theme = an EDBMasterTheme value mapping directly to a PrimeNG prebuilt theme stylesheet name (e.g. md-dark-deeppurple β†’ PrimeNG md-dark-deeppurple). Families include Bootstrap4, MD (Material), MDC, Fluent, Lara, Soho, Viva, Mira, Nano, Saga, Vela, Arya, Nova, Luna, Rhea β€” with light/dark variants and color accents (blue/indigo/purple/teal/green/orange/amber/pink). Load the corresponding PrimeNG theme CSS at runtime.
  • showThemeSelector (default false) β†’ render a theme switcher in the grid header that swaps the loaded theme stylesheet (theme.service.ts).
  • Global defaults to carry: dateTimeFormat (default 'dd-MM-yyyy hh:mm:ss a'), dateTimeZone (default browser tz).

12. Iframe / parent-app integration

UI Maker pages run in an iframe and communicate via window.postMessage with { eventType, eventData }. Implement parent-integration.service.ts to send/receive these. Handshake: iframe emits PAGE_READY; parent sets config (append-query/global data) then sends PARENT_READY. With wait-for-parent-ready=true, the page blocks until PARENT_READY.

Query params (currently from URL; if not iframe-embedded, take as @Input/route/query params): admin-path, db-master-name, x-am-authorization, x-am-user-authorization, x-am-internationalization, wait-for-parent-ready, show-logs.

Iframe β†’ Parent events (IDBMasterEventFromIframeToParent): PAGE_READY, RECORD_SAVED, RECORDS_IMPORTED, RECORD_UPDATED, RECORD_DELETED, MANY_RECORD_DELETED, GRID_EXPORTED, GRID_REFRESHED, ADD_NEW_BUTTON_CLICKED, CUSTOM_ACTION_BUTTON_CLICKED, DROPDOWN_ADD_NEW_RECORD_SAVED. Plus custom events via window.top.postMessage({ eventType, eventData }, '*').

Parent β†’ Iframe events (IDBMasterEventFromParentToIframe): TRIGGER_ADD_NEW_BUTTON_CLICK, TRIGGER_REFRESH_BUTTON_CLICK, DATA_TO_APPEND_IN_GRID_LOAD_FIND_QUERY (merge into grid find query), DATA_TO_APPEND_IN_RECORD_SAVE_API_PAYLOAD (merge into create payload), DATA_TO_APPEND_IN_RECORD_UPDATE_API_PAYLOAD (merge into update payload), SET_GLOBAL_DATA_TO_USE_IN_ANY_SCRIPT (populate globalData), PARENT_READY.

The data layer must support merging an externally-supplied object into (a) the grid load/find query, (b) the save payload, and (c) the update payload. If replacing the iframe with normal Angular: expose @Input() appendToFindQuery, @Input() appendToSavePayload, @Input() appendToUpdatePayload, @Input() globalData, @Output() emitters for each iframe→parent event, and methods triggerAddNew() / triggerRefresh(). Preserve wait-for-parent-ready semantics (don't load until inputs are set) and show-logs. (For real iframe binding in Angular, sanitize the URL with DomSanitizer.bypassSecurityTrustResourceUrl.)


13. Date & timezone handling

  • All dates use date-fns formats. Global dateTimeFormat default 'dd-MM-yyyy hh:mm:ss a'; global dateTimeZone default browser tz. Dates are converted to dateTimeZone before grid display AND before being sent into the DB query (use date-fns-tz).
  • Grid columns: autoFormatDate:true formats the cell (default 'dd-MM-yyyy hh:mm:ss a'); per-column dateTimeFormat/dateTimeZone override.
  • date_picker value is a JS Date. PrimeNG calendar format caveat: PrimeNG's dateFormat token syntax differs from date-fns β€” convert (e.g. dd-MM-yyyy β†’ dd-mm-yy). PrimeNG calendar has no time format token, so time is auto-appended to the displayed value. Unsupported PrimeNG calendar tokens will break β€” validate during conversion.
  • For view='month'/'year', keep dateTimeFormat consistent ('MM-yyyy'/'yyyy').
  • Centralize all of this in date-format.service.ts (date-fns format string, date-fns β†’ PrimeNG token conversion, tz conversion).

14. Step-by-step conversion procedure (per UI page)

For each UI Maker page:

  1. Read the page's dbMasterConfig in full β€” it is authoritative. List every key actually used (don't port unused keys).
  2. Scaffold the page component set under pages/<db-master-name>/ (.ts/.html/.scss, .main.ts, .api.ts, .model.ts). Import the shared library.
  3. Build the FormGroup from form.fields[][]: create nested FormGroups for dotted paths; lay out rows/columns with cssClassDiv (compute col-md-* from columns count).
  4. Wire validations: static required β†’ Validators.required; control-specific (minLength, maxLength, min, max, email, enum, custom validatorFun) β†’ matching validators; requiredFun β†’ dynamic validator re-evaluated on valueChanges (takes precedence over static required). Surface validationErrors.* messages.
  5. Wire dropdowns/cascades: implement dataSource (static/db/api), optionLabel/optionValue (HTML labels via templates), filter/filterBy/filterMatchMode, virtualScroll, alwaysGetLatestDataOnFormOpen, addNewFormConfig, and reloadDropdownsOfPath + isDependentOnPath + modify*Request cascades (Β§8).
  6. Wire file uploads (Β§7.17): upload/download/remove contracts, placeholder substitution, response shape.
  7. Port jsCode hooks: page-level (Β§4.2) β†’ component methods/lifecycle; per-control β†’ control event handlers. Resolve userUtils.Main.* (page) and userUtils.GeneralUtils.* (shared). Support string/function/reference, Promise, returned-function.
  8. Port cssCode β†’ page <name>.scss; common CSS β†’ global styles.scss.
  9. Wire the API service: standard endpoints + every apiCallOverrides (POST only, placeholder substitution, before/after hooks).
  10. Wire the grid (Β§5): columns, sorting, filtering, global search, pagination, grouping, selection, row actions + ordering + magic-hide flags, toolbar ops + ordering, custom action buttons + confirmationMessageScript.
  11. Wire CSV import/export (Β§10) and theme (Β§11).
  12. Wire parent integration (Β§12): postMessage events / @Input/@Output bridge, append-query/save/update payload merging, globalData.
  13. Verify against the original running page (Β§15) β€” behavior, not just structure.

15. Definition of Done / verification checklist

Tick every item for the page. This is the safety net for "do not miss any feature."

Form controls β€” every control present in the config is rendered correctly: - [ ] input, textarea, password, inputNumber, inputMask, inputOtp, date_picker, checkbox, radio - [ ] dropdown, auto_complete, multi_select (all three data sources: static_data / db_data / api_call) - [ ] color_picker, editor (Quill formats whitelist), rating (custom icons), knob - [ ] file_upload, image, button, divider, customHTML (sanitized), accordion, tab_view, nested grid

Per-control fidelity: - [ ] Every settings property mapped (no property dropped) - [ ] optionLabel/optionValue correct (auto_complete stores label-value, no optionValue) - [ ] filterMatchMode set per control (autoComplete only contains/startsWith/endsWith) - [ ] HTML in optionLabel/helpText/headers rendered via [innerHTML] - [ ] inputMask model mutation done in complete, not blur - [ ] inputNumber currency requires currency; locale default en-US; buttonLayout default stacked - [ ] date_picker value is Date; PrimeNG format conversion; month/year view formats; time auto-appended

Validation: - [ ] static required, dynamic requiredFun (precedence), validationErrors messages - [ ] control-specific minLength/maxLength/min/max/minFractionDigits/email/enum/validatorFun - [ ] selectionLimit, forceSelection, maxFileSize, fileLimit, accept

Conditional logic: - [ ] visible / disabled as boolean OR JS-expression string, re-evaluated on value changes - [ ] requiredFun re-evaluated dynamically - [ ] tab/accordion disabled (accordion: bool|expr; tabView: bool only)

Dependent / cascading dropdowns: - [ ] reloadDropdownsOfPath, isDependentOnPath, modify*Request working N levels, mixed control types - [ ] children cleared + reloaded; parent value injected into child query; once*DataLoaded runs

File upload: - [ ] upload (files field, returns [{originalName, uploadPath}]), download/preview (base64), remove ({originalName, uploadPath}) - [ ] auto vs manual, multiple, accept, maxFileSize (10MB default), fileLimit, :beHostPort/:userPath

Grid: - [ ] columns (auto-from-schema excludes _id), header/path/dotted paths, sorting, visibilityStatus, autoFormatDate/dateTimeFormat, dbData FK lookup, cssClass - [ ] sortMode/multiSortMeta, pagination (showPagination/serverSide/rowsPerPage/options/report template), grouping (subheader), selection (mode/dataKey/metaKeySelection/checkboxSelection) - [ ] filter (showGlobalSearch/globalSearchFields string-contains/number-exact, rowFilter), selectFieldsForQuery, minWidth/breakpoint, column selector - [ ] row ops (edit/view/delete) + cssClass + confirmations + double-click-edit + delete-button-click + gridOperationsOrder/gridColumnsOrder/actionColumnWidth - [ ] per-row magic-hide flags (____hide_delete_button/_edit_/_view_/____hide_<actionName>) - [ ] row custom action buttons + confirmationMessageScript (template-literal eval with selectedGridItems) - [ ] toolbar ops + operationsOrder (default order), add/refresh/delete-all/export/import/column-selector/theme-selector + hide flags + hideDeleteManySuccessMessage; top-level custom action buttons

Nested structures: - [ ] nested fields[][] layout containers; nested grid (gridSettings) as FormArray, unlimited depth; dataFieldPath; addNewFormConfig inline add flow

API & overrides: - [ ] standard endpoints + all apiCallOverrides (add/save, edit, getById, delete, deleteMany, import, grid get-data, dropdown) β€” POST only - [ ] :beHostPort/:userPath substitution; codeBeforeAPICall/codeAfterAPICall with correct scope vars; pkField

CSV / theming / dates: - [ ] CSV import (Papa Parse, fieldMappings, type conversion, date parse/tz, UI display formats, preview grid widths) + export (all/selected, labels, icon, processDataBeforeExport) - [ ] theme applied + showThemeSelector; global dateTimeFormat/dateTimeZone defaults; date-fns ↔ PrimeNG conversion

JS / CSS hooks: - [ ] page-level jsCode hooks (all EDBMasterConfigAppendTo used) with correct scope (config, globalData, utils, queryParams, gridEvent, gridData, reqBody, formData, mode) - [ ] per-control jsCode events wired to PrimeNG outputs; string/function/userUtils.* refs; Promise/returned-function support - [ ] utils helpers (findFormElement, getFlatArrayOfAllFormFields[IncludingGridFields], messageService.*, errorsMap) - [ ] common CSS β†’ global styles; page CSS β†’ page styles; common JS β†’ GeneralUtils; page JS β†’ Main - [ ] external libs (externalLibs.css/js CDNs loaded before user code); inbuilt libs available: Papa, dateFns, dateFnsTz, _, moment; Bootstrap 5.3.3 grid+utilities

Parent integration: - [ ] all iframe→parent and parent→iframe events; handshake (PAGE_READY/PARENT_READY); wait-for-parent-ready; show-logs - [ ] append-data merging into find query / save payload / update payload; SET_GLOBAL_DATA_TO_USE_IN_ANY_SCRIPT → globalData; query params

When any item cannot be satisfied from the config alone, inspect the original UI Maker page at runtime and reproduce the observed behavior exactly. Do not skip, do not approximate.