@isi-ui7/bos7-shared 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/crud-components.d.ts +2 -0
- package/dist/crud-hooks.d.ts +1 -1
- package/dist/crud-types.d.ts +0 -2
- package/dist/form-numeric.d.ts +12 -0
- package/dist/form-renderer.d.ts +4 -2
- package/dist/form-types.d.ts +61 -2
- package/dist/form-value-schema.d.ts +118 -0
- package/dist/index.d.ts +1 -0
- package/dist/{index.es-B4p6mwaz.js → index.es-Bylk5fh-.js} +1 -1
- package/dist/index.js +4766 -4535
- package/dist/{jspdf.es.min-CpRUhh3E.js → jspdf.es.min-Dzm5j8wC.js} +2 -2
- package/dist/{jspdf.plugin.autotable-DFaknRns.js → jspdf.plugin.autotable-DcntYaes.js} +22 -19
- package/dist/purify.es-CiEWEeUM.js +605 -0
- package/dist/shell/app-shell-layout.d.ts +20 -5
- package/dist/style-contract.d.ts +20 -5
- package/dist/workflow/starter.d.ts +13 -0
- package/package.json +10 -7
- package/src/crud-components.tsx +27 -2
- package/src/crud-hooks.ts +5 -2
- package/src/crud-types.ts +0 -2
- package/src/form-contract.css +64 -2
- package/src/form-numeric.ts +16 -0
- package/src/form-renderer.tsx +303 -12
- package/src/form-types.ts +67 -2
- package/src/form-value-schema.test.ts +263 -0
- package/src/form-value-schema.ts +495 -0
- package/src/index.ts +1 -0
- package/src/shell/app-shell-layout.tsx +20 -44
- package/src/style-contract.test.ts +1 -0
- package/src/style-contract.ts +38 -5
- package/src/workflow/starter.ts +48 -11
- package/dist/purify.es-Cm3utOpm.js +0 -560
|
@@ -3,6 +3,8 @@ import { CrudDeleteSchema, CrudListSchema } from './crud-types';
|
|
|
3
3
|
import { CrudForm, FormMode } from './form-types';
|
|
4
4
|
import { Ui7FormDensity } from './style-contract';
|
|
5
5
|
|
|
6
|
+
export { buildValueSections, buildXRulesValidator, } from './form-value-schema';
|
|
7
|
+
export type { JSONSchema, XUi, XUiRoot, XUiNumeric, XUiOption, XUiWidget, XRule, XRuleOp, ValueValidatorFn, } from './form-value-schema';
|
|
6
8
|
export declare function SharedCrudListPage({ schema, tableKey, onAdd, onPopupClick }: {
|
|
7
9
|
schema: CrudListSchema;
|
|
8
10
|
tableKey?: number;
|
package/dist/crud-hooks.d.ts
CHANGED
|
@@ -12,4 +12,4 @@ export declare function applyCrudFieldOverrides<TField extends {
|
|
|
12
12
|
readonly?: boolean;
|
|
13
13
|
required?: boolean;
|
|
14
14
|
helperText?: unknown;
|
|
15
|
-
}>(fields: TField[], hooks?: CrudActionOverrideHooks<
|
|
15
|
+
}, TData extends Record<string, unknown> = Record<string, unknown>>(fields: TField[], hooks?: CrudActionOverrideHooks<TData>): TField[];
|
package/dist/crud-types.d.ts
CHANGED
|
@@ -63,8 +63,6 @@ export type CrudListSchema = {
|
|
|
63
63
|
showSort?: boolean;
|
|
64
64
|
/** Enable search bar. Defaults to true. */
|
|
65
65
|
showSearch?: boolean;
|
|
66
|
-
/** Endpoint for LookupInput (defaults to apiPath if omitted). In Pattern B both DataTable and LookupInput share the same /query endpoint. */
|
|
67
|
-
lookupApiPath?: string;
|
|
68
66
|
/**
|
|
69
67
|
* Level 2: schema-driven confirmation modals for custom popup actions.
|
|
70
68
|
* Keyed by popup menu item id. Actions not listed here fall through to onCustomAction (Level 1).
|
package/dist/form-numeric.d.ts
CHANGED
|
@@ -33,3 +33,15 @@ export declare const INDONESIA_PHONE_RULE: PhoneRule;
|
|
|
33
33
|
export declare const GLOBAL_PHONE_RULE: PhoneRule;
|
|
34
34
|
/** Standard currency precision for Indonesian banking (IDR 2 dec, USD 4 dec). */
|
|
35
35
|
export declare const INDONESIA_CURRENCY_CONFIG: CurrencyPrecisionConfig;
|
|
36
|
+
/**
|
|
37
|
+
* Whole-rupiah currency config — used for ledger-style fields like plafond,
|
|
38
|
+
* subsidi, agent limit, where the convention in Indonesian retail banking is
|
|
39
|
+
* to round to whole IDR (no sen). USD/USD-denominated fields still keep 2
|
|
40
|
+
* decimals (standard cents). Default precision is 0 so any other currency
|
|
41
|
+
* defaults to whole units in these contexts.
|
|
42
|
+
*
|
|
43
|
+
* Use this for "agent plafond / saldo / subsidi" style fields. For amounts
|
|
44
|
+
* that genuinely need sub-rupiah precision (e.g. percentage-derived fees,
|
|
45
|
+
* accrued interest), prefer `INDONESIA_CURRENCY_CONFIG` instead.
|
|
46
|
+
*/
|
|
47
|
+
export declare const INDONESIA_RUPIAH_WHOLE_CONFIG: CurrencyPrecisionConfig;
|
package/dist/form-renderer.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ui7FormDensity } from './style-contract';
|
|
1
|
+
import { Ui7FormDensity, Ui7FormWidth } from './style-contract';
|
|
2
2
|
import { FormMode, FormSection } from './form-types';
|
|
3
3
|
|
|
4
4
|
export type SchemaFormRendererProps<TData extends Record<string, unknown>> = {
|
|
@@ -14,5 +14,7 @@ export type SchemaFormRendererProps<TData extends Record<string, unknown>> = {
|
|
|
14
14
|
scopeClassName?: string;
|
|
15
15
|
/** Form layout density. Defaults to "compact". */
|
|
16
16
|
density?: Ui7FormDensity;
|
|
17
|
+
/** Desktop form-panel width. Defaults to "two-thirds". */
|
|
18
|
+
width?: Ui7FormWidth;
|
|
17
19
|
};
|
|
18
|
-
export declare function SchemaFormRenderer<TData extends Record<string, unknown>>({ mode, value, sections, errors, disabled, onChange, className, showSectionHeader, scopeClassName, density, }: SchemaFormRendererProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare function SchemaFormRenderer<TData extends Record<string, unknown>>({ mode, value, sections, errors, disabled, onChange, className, showSectionHeader, scopeClassName, density, width, }: SchemaFormRendererProps<TData>): import("react/jsx-runtime").JSX.Element;
|
package/dist/form-types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { Ui7FormDensity } from './style-contract';
|
|
2
|
+
import { Ui7FormDensity, Ui7FormWidth } from './style-contract';
|
|
3
3
|
import { T_LookupTblStruct } from '@isi-ui7/lookup-input';
|
|
4
|
+
import { EditableColumnDef } from '@isi-ui7/editable-table';
|
|
4
5
|
|
|
5
6
|
export type FormMode = "create" | "edit" | "view";
|
|
6
7
|
export type ApiPath = string | ((params: Record<string, string>) => string);
|
|
@@ -18,7 +19,7 @@ export type FieldValidation<TData extends Record<string, unknown>> = {
|
|
|
18
19
|
/** Array of validators — compose Rules.* or custom functions. Runs after built-in rules. */
|
|
19
20
|
validators?: FieldValidatorFn<TData>[];
|
|
20
21
|
};
|
|
21
|
-
export type FormFieldType = "text" | "textarea" | "number" | "date" | "select" | "lookup" | "checkbox" | "toggle";
|
|
22
|
+
export type FormFieldType = "text" | "textarea" | "number" | "date" | "select" | "lookup" | "checkbox" | "toggle" | "detail-rows";
|
|
22
23
|
export type FormFieldOption = {
|
|
23
24
|
value: string;
|
|
24
25
|
label: string;
|
|
@@ -33,6 +34,17 @@ export type FormFieldLookupConfig<TData extends Record<string, unknown>> = {
|
|
|
33
34
|
dataSource?: "api" | "direct";
|
|
34
35
|
/** Return a patch to merge into the form when a lookup row is selected. */
|
|
35
36
|
onDataPatch?: (row: Record<string, unknown>, data: TData) => Partial<TData>;
|
|
37
|
+
/**
|
|
38
|
+
* Form field keys whose values combine into the initial display text on
|
|
39
|
+
* Edit load. Example: `["kode_cabang","branch_name"]` renders
|
|
40
|
+
* "001 KANTOR CABANG BANDUNG" instead of just "001". Typical pairing
|
|
41
|
+
* with an onDataPatch that stashes the picked row's name into a
|
|
42
|
+
* companion field. If any referenced field is empty, the lookup falls
|
|
43
|
+
* back to the field's own primary-key value.
|
|
44
|
+
*/
|
|
45
|
+
initialDisplayFields?: (keyof TData)[];
|
|
46
|
+
/** Separator joining initialDisplayFields. Default: " " */
|
|
47
|
+
initialDisplaySeparator?: string;
|
|
36
48
|
};
|
|
37
49
|
export type FormFieldNumericConfig<TData extends Record<string, unknown>> = {
|
|
38
50
|
kind: "currency";
|
|
@@ -50,11 +62,51 @@ export type FormFieldNumericConfig<TData extends Record<string, unknown>> = {
|
|
|
50
62
|
maxDigits: number;
|
|
51
63
|
allowedPrefixes?: string[];
|
|
52
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* Toggle field state mapping for non-boolean stored values.
|
|
67
|
+
*
|
|
68
|
+
* Example for a "Y" / "N" string column:
|
|
69
|
+
* { type: "toggle", toggle: { valueOn: "Y", valueOff: "N" } }
|
|
70
|
+
*
|
|
71
|
+
* Toggled = (current value === valueOn). Flipping the toggle writes
|
|
72
|
+
* either `valueOn` or `valueOff` into the form data. Defaults to
|
|
73
|
+
* boolean true / false when omitted.
|
|
74
|
+
*/
|
|
75
|
+
export type FormFieldToggleConfig = {
|
|
76
|
+
valueOn?: unknown;
|
|
77
|
+
valueOff?: unknown;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Detail-rows config — embeds an inline `<EditableTable>` inside the form for
|
|
81
|
+
* master-detail entry. The field value at `key` must be an array of plain
|
|
82
|
+
* objects (e.g. `details: BucketRow[]`). The renderer reads / writes that
|
|
83
|
+
* array atomically via `onChange({ ...data, [key]: newRows })`.
|
|
84
|
+
*
|
|
85
|
+
* `columns`, `newRowFactory`, `validateRow`, `maxRows` mirror the props of
|
|
86
|
+
* `<EditableTable>` from `@isi-ui7/editable-table`.
|
|
87
|
+
*/
|
|
88
|
+
export type DetailRowsConfig = {
|
|
89
|
+
columns: EditableColumnDef[];
|
|
90
|
+
newRowFactory: () => Record<string, unknown>;
|
|
91
|
+
validateRow?: (row: Record<string, unknown>, index: number) => Record<string, string>;
|
|
92
|
+
maxRows?: number;
|
|
93
|
+
};
|
|
53
94
|
export type FormField<TData extends Record<string, unknown>> = {
|
|
54
95
|
key: keyof TData;
|
|
55
96
|
label: ReactNode;
|
|
56
97
|
type: FormFieldType;
|
|
57
98
|
span?: number;
|
|
99
|
+
/**
|
|
100
|
+
* Force this field to start a new row even when the current row still
|
|
101
|
+
* has space for its `span`. Useful to break a logical group onto its
|
|
102
|
+
* own line (e.g. a wide picker that should sit alone, or a sub-group
|
|
103
|
+
* header pattern within one section).
|
|
104
|
+
*
|
|
105
|
+
* Implemented via `gridColumnStart: 1` — the CSS grid skips the
|
|
106
|
+
* remaining columns of the previous row and places this field at the
|
|
107
|
+
* left edge of a new row.
|
|
108
|
+
*/
|
|
109
|
+
breakBefore?: boolean;
|
|
58
110
|
/** Static or dynamic readonly. Function receives current mode + form data. */
|
|
59
111
|
readonly?: boolean | ((mode: FormMode, data: TData) => boolean);
|
|
60
112
|
/** Static or dynamic visibility. Hidden fields are skipped in validation. */
|
|
@@ -69,6 +121,8 @@ export type FormField<TData extends Record<string, unknown>> = {
|
|
|
69
121
|
options?: FormFieldOption[];
|
|
70
122
|
lookup?: FormFieldLookupConfig<TData>;
|
|
71
123
|
numeric?: FormFieldNumericConfig<TData>;
|
|
124
|
+
toggle?: FormFieldToggleConfig;
|
|
125
|
+
detailRows?: DetailRowsConfig;
|
|
72
126
|
format?: (value: TData[keyof TData], data: TData) => ReactNode;
|
|
73
127
|
validation?: FieldValidation<TData>;
|
|
74
128
|
};
|
|
@@ -119,6 +173,11 @@ export type CrudForm<TData extends Record<string, unknown>> = {
|
|
|
119
173
|
};
|
|
120
174
|
/** Form layout density. Defaults to "compact". */
|
|
121
175
|
density?: Ui7FormDensity;
|
|
176
|
+
/**
|
|
177
|
+
* Desktop form-panel width — "half" | "two-thirds" | "full".
|
|
178
|
+
* Default "two-thirds". Tablet + mobile (<1056px) always stretch full.
|
|
179
|
+
*/
|
|
180
|
+
width?: Ui7FormWidth;
|
|
122
181
|
emptyData: TData;
|
|
123
182
|
createApiPath: ApiPath;
|
|
124
183
|
updateApiPath?: ApiPath;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { FormMode, FormSection } from './form-types';
|
|
2
|
+
|
|
3
|
+
export type T = (key: string, fallback?: string) => string;
|
|
4
|
+
export type XUiNumeric = {
|
|
5
|
+
kind: "currency" | "percent" | "integer" | "phone";
|
|
6
|
+
/** currency: name of the sibling field holding the currency code. */
|
|
7
|
+
currencyField?: string;
|
|
8
|
+
precisionByCurrency?: Record<string, number>;
|
|
9
|
+
defaultPrecision?: number;
|
|
10
|
+
/** percent. */
|
|
11
|
+
maxFractionDigits?: number;
|
|
12
|
+
/** phone. */
|
|
13
|
+
minDigits?: number;
|
|
14
|
+
maxDigits?: number;
|
|
15
|
+
allowedPrefixes?: string[];
|
|
16
|
+
};
|
|
17
|
+
export type XUiOption = {
|
|
18
|
+
value: string;
|
|
19
|
+
labelKey?: string;
|
|
20
|
+
label?: string;
|
|
21
|
+
};
|
|
22
|
+
export type XUiWidget = "text" | "textarea" | "number" | "select" | "date" | "toggle" | "lookup" | "detail-rows";
|
|
23
|
+
/** Per-property `x-ui` hint object (also array-level for `detail-rows`). */
|
|
24
|
+
export type XUi = {
|
|
25
|
+
widget?: XUiWidget;
|
|
26
|
+
labelKey?: string;
|
|
27
|
+
label?: string;
|
|
28
|
+
helpKey?: string;
|
|
29
|
+
help?: string;
|
|
30
|
+
/** 1–12 grid width. Default 6. */
|
|
31
|
+
span?: number;
|
|
32
|
+
/** Sort order within an inferred section. */
|
|
33
|
+
order?: number;
|
|
34
|
+
/** Modes in which the field renders read-only, e.g. ["edit"]. */
|
|
35
|
+
readonlyOn?: FormMode[];
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
numeric?: XUiNumeric;
|
|
38
|
+
/** `select` options (overrides enum labels). */
|
|
39
|
+
options?: XUiOption[];
|
|
40
|
+
/** Named dynamic-options source — deferred in Wave C (resolved by FE). */
|
|
41
|
+
optionsRef?: string;
|
|
42
|
+
lookup?: {
|
|
43
|
+
api: string;
|
|
44
|
+
valueField?: string;
|
|
45
|
+
displayFields?: string[];
|
|
46
|
+
};
|
|
47
|
+
toggle?: {
|
|
48
|
+
valueOn?: unknown;
|
|
49
|
+
valueOff?: unknown;
|
|
50
|
+
};
|
|
51
|
+
/** array `detail-rows`: max number of rows. */
|
|
52
|
+
maxRows?: number;
|
|
53
|
+
};
|
|
54
|
+
/** Root-level `x-ui` (layout grouping + density). */
|
|
55
|
+
export type XUiRoot = XUi & {
|
|
56
|
+
layout?: {
|
|
57
|
+
sections?: Array<{
|
|
58
|
+
titleKey?: string;
|
|
59
|
+
title?: string;
|
|
60
|
+
fields: string[];
|
|
61
|
+
}>;
|
|
62
|
+
};
|
|
63
|
+
density?: string;
|
|
64
|
+
};
|
|
65
|
+
export type JSONSchema = {
|
|
66
|
+
type?: string;
|
|
67
|
+
properties?: Record<string, JSONSchema>;
|
|
68
|
+
required?: string[];
|
|
69
|
+
enum?: unknown[];
|
|
70
|
+
minimum?: number;
|
|
71
|
+
maximum?: number;
|
|
72
|
+
minLength?: number;
|
|
73
|
+
maxLength?: number;
|
|
74
|
+
pattern?: string;
|
|
75
|
+
format?: string;
|
|
76
|
+
default?: unknown;
|
|
77
|
+
items?: JSONSchema;
|
|
78
|
+
"x-ui"?: XUiRoot;
|
|
79
|
+
"x-rules"?: XRule[];
|
|
80
|
+
};
|
|
81
|
+
export type XRuleOp = "lte" | "gte" | "lt" | "gt" | "eq" | "required-if";
|
|
82
|
+
export type XRule = {
|
|
83
|
+
op: XRuleOp;
|
|
84
|
+
left: string;
|
|
85
|
+
/** Other field to compare against (lte/gte/lt/gt/eq, or the condition field of required-if). */
|
|
86
|
+
right?: string;
|
|
87
|
+
/** Literal to compare against (eq) or the condition value (required-if). */
|
|
88
|
+
value?: unknown;
|
|
89
|
+
message?: string;
|
|
90
|
+
/** i18n key for the message (preferred over `message`). */
|
|
91
|
+
messageKey?: string;
|
|
92
|
+
};
|
|
93
|
+
type ValueData = Record<string, unknown>;
|
|
94
|
+
export type ValueValidatorFn = (data: ValueData) => Partial<Record<string, string>>;
|
|
95
|
+
/**
|
|
96
|
+
* Build a form-level validator from `x-rules`. Comparison ops skip when either
|
|
97
|
+
* operand is empty/non-numeric (per-field `required` handles emptiness), so the
|
|
98
|
+
* cross-field check only fires on otherwise-complete input. Returns errors keyed
|
|
99
|
+
* by the offending field so the page blocks submit before `startWorkflow`.
|
|
100
|
+
*/
|
|
101
|
+
export declare function buildXRulesValidator(rules: XRule[] | undefined, t: T): ValueValidatorFn;
|
|
102
|
+
/**
|
|
103
|
+
* Build the "value" form section(s) + a cross-field validator from a policy7
|
|
104
|
+
* `value_schema`. Pure; no fetch.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* const { sections, validate } = buildValueSections(valueSchema, t);
|
|
108
|
+
* const form: CrudForm<Data> = {
|
|
109
|
+
* ...,
|
|
110
|
+
* layout: { type: "single-page", sections: [scopeSection(t), ...sections] },
|
|
111
|
+
* validate: (data) => ({ ...scopeValidate(data), ...validate(data) }),
|
|
112
|
+
* };
|
|
113
|
+
*/
|
|
114
|
+
export declare function buildValueSections(valueSchema: JSONSchema, t: T): {
|
|
115
|
+
sections: FormSection<ValueData>[];
|
|
116
|
+
validate: ValueValidatorFn;
|
|
117
|
+
};
|
|
118
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from './notifications-bff';
|
|
|
15
15
|
export * from './crud-types';
|
|
16
16
|
export * from './crud-hooks';
|
|
17
17
|
export * from './form-types';
|
|
18
|
+
export * from './form-value-schema';
|
|
18
19
|
export * from './i18n';
|
|
19
20
|
export { proxyBackendPost, proxyQueryRoute } from './data-table/proxy';
|
|
20
21
|
export type { ProxyBackendPostOptions, ProxyQueryRouteOptions } from './data-table/proxy';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as Xa } from "./jspdf.es.min-
|
|
1
|
+
import { _ as Xa } from "./jspdf.es.min-Dzm5j8wC.js";
|
|
2
2
|
var Ke = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {};
|
|
3
3
|
function Do(a) {
|
|
4
4
|
return a && a.__esModule && Object.prototype.hasOwnProperty.call(a, "default") ? a.default : a;
|