@isi-ui7/bos7-shared 0.2.4 → 0.2.7
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/form-numeric.d.ts +12 -0
- package/dist/form-renderer.d.ts +4 -2
- package/dist/form-types.d.ts +75 -3
- package/dist/form-value-schema.d.ts +118 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4768 -4534
- package/dist/shell/app-shell-layout.d.ts +21 -6
- 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/form-contract.css +64 -2
- package/src/form-numeric.ts +16 -0
- package/src/form-renderer.tsx +381 -18
- package/src/form-types.ts +83 -5
- package/src/form-value-schema.test.ts +263 -0
- package/src/form-value-schema.ts +500 -0
- package/src/index.ts +1 -0
- package/src/shell/app-shell-layout.tsx +22 -46
- package/src/style-contract.test.ts +1 -0
- package/src/style-contract.ts +38 -5
- package/src/workflow/starter.ts +48 -11
package/src/style-contract.ts
CHANGED
|
@@ -2,6 +2,17 @@ export type Ui7FormDensity = "compact" | "comfortable";
|
|
|
2
2
|
|
|
3
3
|
export type Ui7ThemeDensity = Ui7FormDensity | "normal";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Desktop form-panel width. Mobile + tablet (<1056px) always release the
|
|
7
|
+
* cap and stretch to 100% — the modifier only matters at ≥1056px.
|
|
8
|
+
*
|
|
9
|
+
* - "half" → 50% of page body
|
|
10
|
+
* - "two-thirds" → 66.6667% (default — readable column width on wide
|
|
11
|
+
* monitors, what the contract shipped with originally)
|
|
12
|
+
* - "full" → 100% (forms with many columns or wide tables inside)
|
|
13
|
+
*/
|
|
14
|
+
export type Ui7FormWidth = "half" | "two-thirds" | "full";
|
|
15
|
+
|
|
5
16
|
export interface Ui7FormVisualTokens {
|
|
6
17
|
labelSpacing: "6px";
|
|
7
18
|
labelFontSize: "12px";
|
|
@@ -58,6 +69,9 @@ export const UI7_FORM_VISUAL_CLASSNAMES = {
|
|
|
58
69
|
scope: "ui7-form-contract",
|
|
59
70
|
densityCompact: "ui7-form-contract--density-compact",
|
|
60
71
|
densityComfortable: "ui7-form-contract--density-comfortable",
|
|
72
|
+
widthHalf: "ui7-form-contract--width-half",
|
|
73
|
+
widthTwoThirds: "ui7-form-contract--width-two-thirds",
|
|
74
|
+
widthFull: "ui7-form-contract--width-full",
|
|
61
75
|
readonly: "ui7-form-contract--readonly",
|
|
62
76
|
row: "ui7-form-row",
|
|
63
77
|
section: "ui7-form-section",
|
|
@@ -76,15 +90,15 @@ export const UI7_FORM_VISUAL_CLASSNAMES = {
|
|
|
76
90
|
export const UI7_FORM_DENSITY_TOKENS = {
|
|
77
91
|
compact: {
|
|
78
92
|
controlHeight: "2rem",
|
|
79
|
-
rowGap: "
|
|
80
|
-
sectionGap: "
|
|
81
|
-
columnGap: "
|
|
93
|
+
rowGap: "1rem",
|
|
94
|
+
sectionGap: "1.25rem",
|
|
95
|
+
columnGap: "1rem",
|
|
82
96
|
},
|
|
83
97
|
comfortable: {
|
|
84
98
|
controlHeight: "2.5rem",
|
|
85
|
-
rowGap: "
|
|
99
|
+
rowGap: "1.25rem",
|
|
86
100
|
sectionGap: "1.5rem",
|
|
87
|
-
columnGap: "
|
|
101
|
+
columnGap: "1.25rem",
|
|
88
102
|
},
|
|
89
103
|
} as const;
|
|
90
104
|
|
|
@@ -107,9 +121,24 @@ export function getUi7FormDensityClassName(
|
|
|
107
121
|
: UI7_FORM_VISUAL_CLASSNAMES.densityComfortable;
|
|
108
122
|
}
|
|
109
123
|
|
|
124
|
+
export function getUi7FormWidthClassName(width?: Ui7FormWidth | null): string {
|
|
125
|
+
switch (width) {
|
|
126
|
+
case "half":
|
|
127
|
+
return UI7_FORM_VISUAL_CLASSNAMES.widthHalf;
|
|
128
|
+
case "full":
|
|
129
|
+
return UI7_FORM_VISUAL_CLASSNAMES.widthFull;
|
|
130
|
+
case "two-thirds":
|
|
131
|
+
default:
|
|
132
|
+
// Default is two-thirds — kept explicit (not relying on base CSS) so
|
|
133
|
+
// a future base-width change won't silently shift every form.
|
|
134
|
+
return UI7_FORM_VISUAL_CLASSNAMES.widthTwoThirds;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
110
138
|
export function getUi7FormContractClassName(
|
|
111
139
|
options?: {
|
|
112
140
|
density?: Ui7ThemeDensity | null;
|
|
141
|
+
width?: Ui7FormWidth | null;
|
|
113
142
|
readonly?: boolean;
|
|
114
143
|
className?: string | null;
|
|
115
144
|
}
|
|
@@ -120,6 +149,10 @@ export function getUi7FormContractClassName(
|
|
|
120
149
|
classes.push(getUi7FormDensityClassName(options.density));
|
|
121
150
|
}
|
|
122
151
|
|
|
152
|
+
// Width: always emit a modifier so the choice is explicit at the DOM
|
|
153
|
+
// level. Default = two-thirds (matches what the contract shipped with).
|
|
154
|
+
classes.push(getUi7FormWidthClassName(options?.width));
|
|
155
|
+
|
|
123
156
|
if (options?.readonly) {
|
|
124
157
|
classes.push(UI7_FORM_VISUAL_CLASSNAMES.readonly);
|
|
125
158
|
}
|
package/src/workflow/starter.ts
CHANGED
|
@@ -18,15 +18,22 @@ type CookieReqLike = Request & {
|
|
|
18
18
|
cookies?: { get?: (name: string) => { value?: string } | undefined };
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
type Auth7Claims = {
|
|
22
|
+
sub?: string;
|
|
23
|
+
preferred_username?: string;
|
|
24
|
+
org_id?: string;
|
|
25
|
+
branch_id?: string;
|
|
26
|
+
branch_code?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function decodeJwtClaims(token: string): Auth7Claims {
|
|
22
30
|
try {
|
|
23
31
|
const parts = token.split('.');
|
|
24
|
-
if (parts.length < 2) return
|
|
32
|
+
if (parts.length < 2) return {};
|
|
25
33
|
const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
26
|
-
|
|
27
|
-
return claims.sub ?? '';
|
|
34
|
+
return JSON.parse(Buffer.from(payload, 'base64').toString('utf-8')) as Auth7Claims;
|
|
28
35
|
} catch {
|
|
29
|
-
return
|
|
36
|
+
return {};
|
|
30
37
|
}
|
|
31
38
|
}
|
|
32
39
|
|
|
@@ -51,7 +58,23 @@ export type StartWorkflowInput = {
|
|
|
51
58
|
flowId: string;
|
|
52
59
|
payload: Record<string, unknown>;
|
|
53
60
|
externalRef?: string;
|
|
54
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Optional override for initiator identity. By default the helper decodes
|
|
63
|
+
* the full tenant identity (user_id, username, org_id, branch_id, branch_code)
|
|
64
|
+
* from the access token and uses that. Any field explicitly set here wins.
|
|
65
|
+
*
|
|
66
|
+
* Set ONLY when the caller has a verified reason to override (rare). For
|
|
67
|
+
* normal flow-start paths leave this undefined — letting the helper read
|
|
68
|
+
* the JWT keeps initiator_context aligned with the auth7 signature.
|
|
69
|
+
*/
|
|
70
|
+
initiatorContext?: {
|
|
71
|
+
user_id?: string;
|
|
72
|
+
username?: string;
|
|
73
|
+
org_id?: string;
|
|
74
|
+
branch_id?: string;
|
|
75
|
+
branch_code?: string;
|
|
76
|
+
ip?: string;
|
|
77
|
+
};
|
|
55
78
|
/** User access token dari auth7 — diforward ke workflow7 sebagai Bearer. */
|
|
56
79
|
userAccessToken: string;
|
|
57
80
|
};
|
|
@@ -64,11 +87,25 @@ export type StartWorkflowResult = {
|
|
|
64
87
|
};
|
|
65
88
|
|
|
66
89
|
export async function startWorkflow(input: StartWorkflowInput): Promise<StartWorkflowResult> {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
// Decode the full tenant identity from the user JWT so workflow7 can pass
|
|
91
|
+
// through all five canonical actor fields to downstream service-task HTTP
|
|
92
|
+
// calls (lib7 ActorEnvelope / Wave 17 audit envelope). Decoding here keeps
|
|
93
|
+
// identity aligned with the auth7 signature — service-task verifiers can
|
|
94
|
+
// trust the envelope without re-checking subject claims.
|
|
95
|
+
const claims = decodeJwtClaims(input.userAccessToken);
|
|
96
|
+
const override = input.initiatorContext ?? {};
|
|
97
|
+
const initiatorContext: Record<string, unknown> = {};
|
|
98
|
+
type InitiatorKey = 'user_id' | 'username' | 'org_id' | 'branch_id' | 'branch_code';
|
|
99
|
+
const setIf = (k: InitiatorKey, fromClaim: string | undefined, fromOverride: unknown) => {
|
|
100
|
+
const v = fromOverride ?? fromClaim;
|
|
101
|
+
if (typeof v === 'string' && v !== '') initiatorContext[k] = v;
|
|
102
|
+
};
|
|
103
|
+
setIf('user_id', claims.sub, override.user_id);
|
|
104
|
+
setIf('username', claims.preferred_username, override.username);
|
|
105
|
+
setIf('org_id', claims.org_id, override.org_id);
|
|
106
|
+
setIf('branch_id', claims.branch_id, override.branch_id);
|
|
107
|
+
setIf('branch_code', claims.branch_code, override.branch_code);
|
|
108
|
+
if (typeof override.ip === 'string' && override.ip !== '') initiatorContext.ip = override.ip;
|
|
72
109
|
|
|
73
110
|
const body: Record<string, unknown> = {
|
|
74
111
|
flow_definition_code: input.flowId,
|