@isi-ui7/bos7-shared 0.2.13 → 0.3.0
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-types.d.ts +29 -0
- package/dist/{index-CPY3DDQN.js → index-1k-PSj-b.js} +363 -359
- package/dist/{index.es-Dy3lNA6h.js → index.es-zuTIhIvp.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{jspdf.es.min-Ds3Fsl60.js → jspdf.es.min-CnDMxfNp.js} +1 -1
- package/package.json +16 -15
- package/src/crud-components.tsx +52 -9
- package/src/crud-types.ts +26 -0
- package/src/data-table/proxy.ts +11 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as Ke, g as Do } from "./index-
|
|
2
|
-
import { _ as Xa } from "./jspdf.es.min-
|
|
1
|
+
import { c as Ke, g as Do } from "./index-1k-PSj-b.js";
|
|
2
|
+
import { _ as Xa } from "./jspdf.es.min-CnDMxfNp.js";
|
|
3
3
|
var vt = function(a) {
|
|
4
4
|
return a && a.Math === Math && a;
|
|
5
5
|
}, _ = (
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as s, a9 as o, A as t, Y as r, R as i, O as n, l, F as c, G as d, E as u, P as S, a3 as h, a2 as A, a1 as T, a0 as k, af as C, K as _, H as O, I as U, J as g, n as N, ah as P, ag as m, Q as E, i as F, D as p, f, y as I, S as R, j as x, V as v, a8 as y, o as L, h as b, L as B, M as D, T as M, W as H, C as V, e as W, z as q, a7 as K, a5 as w, a6 as z, v as G, aa as Q, ab as X, ac as Y, ad as j, k as J, U as Z, w as $, a4 as aa, Z as ea, _ as sa, p as oa, a as ta, N as ra, q as ia, r as na, $ as la, t as ca, ae as da, s as ua, B as Sa, x as ha, b as Aa, u as Ta, ai as ka, X as Ca, d as _a } from "./index-
|
|
1
|
+
import { m as s, a9 as o, A as t, Y as r, R as i, O as n, l, F as c, G as d, E as u, P as S, a3 as h, a2 as A, a1 as T, a0 as k, af as C, K as _, H as O, I as U, J as g, n as N, ah as P, ag as m, Q as E, i as F, D as p, f, y as I, S as R, j as x, V as v, a8 as y, o as L, h as b, L as B, M as D, T as M, W as H, C as V, e as W, z as q, a7 as K, a5 as w, a6 as z, v as G, aa as Q, ab as X, ac as Y, ad as j, k as J, U as Z, w as $, a4 as aa, Z as ea, _ as sa, p as oa, a as ta, N as ra, q as ia, r as na, $ as la, t as ca, ae as da, s as ua, B as Sa, x as ha, b as Aa, u as Ta, ai as ka, X as Ca, d as _a } from "./index-1k-PSj-b.js";
|
|
2
2
|
export {
|
|
3
3
|
s as AUTH7_SCOPES,
|
|
4
4
|
o as AppShellLayout,
|
|
@@ -9216,7 +9216,7 @@ function(n) {
|
|
|
9216
9216
|
var d = h.getContext("2d");
|
|
9217
9217
|
d.fillStyle = "#fff", d.fillRect(0, 0, h.width, h.height);
|
|
9218
9218
|
var m = { ignoreMouse: !0, ignoreAnimation: !0, ignoreDimensions: !0 }, _ = this;
|
|
9219
|
-
return (Kt.canvg ? Promise.resolve(Kt.canvg) : import("./index.es-
|
|
9219
|
+
return (Kt.canvg ? Promise.resolve(Kt.canvg) : import("./index.es-zuTIhIvp.js")).catch(function(k) {
|
|
9220
9220
|
return Promise.reject(new Error("Could not load canvg: " + k));
|
|
9221
9221
|
}).then(function(k) {
|
|
9222
9222
|
return k.default ? k.default : k;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isi-ui7/bos7-shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Shared auth7 and layout primitives for bos7 applications.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"./workflow/use-workflow-tracker": "./src/workflow/use-workflow-tracker.ts",
|
|
36
36
|
"./workflow/crud-client": "./src/workflow/crud-client.ts",
|
|
37
37
|
"./data-table/cursor-adapter": "./src/data-table/cursor-adapter.ts",
|
|
38
|
+
"./data-table/proxy": "./src/data-table/proxy.ts",
|
|
38
39
|
"./notification/routing": "./src/notification/routing.ts",
|
|
39
40
|
"./shell/interaction": "./src/shell/interaction.ts",
|
|
40
41
|
"./shell/branch": "./src/shell/branch.ts",
|
|
@@ -69,14 +70,14 @@
|
|
|
69
70
|
"peerDependencies": {
|
|
70
71
|
"@carbon/icons-react": "^11.71.0",
|
|
71
72
|
"@carbon/react": "^1.97.0",
|
|
72
|
-
"@isi-ui7/corporate-themes": ">=0.2.
|
|
73
|
-
"@isi-ui7/data-table": ">=0.2.
|
|
74
|
-
"@isi-ui7/editable-table": ">=0.2.
|
|
75
|
-
"@isi-ui7/i18n": ">=0.
|
|
76
|
-
"@isi-ui7/lookup-input": ">=0.2.
|
|
73
|
+
"@isi-ui7/corporate-themes": ">=0.2.6",
|
|
74
|
+
"@isi-ui7/data-table": ">=0.2.4",
|
|
75
|
+
"@isi-ui7/editable-table": ">=0.2.3",
|
|
76
|
+
"@isi-ui7/i18n": ">=0.2.3",
|
|
77
|
+
"@isi-ui7/lookup-input": ">=0.2.4",
|
|
77
78
|
"@isi-ui7/modal-manager": ">=0.2.0",
|
|
78
79
|
"@isi-ui7/realtime": ">=0.2.0",
|
|
79
|
-
"@isi-ui7/ui-shell": ">=0.2.
|
|
80
|
+
"@isi-ui7/ui-shell": ">=0.2.8",
|
|
80
81
|
"next": "16.x",
|
|
81
82
|
"react": ">=18",
|
|
82
83
|
"react-dom": ">=18"
|
|
@@ -105,14 +106,14 @@
|
|
|
105
106
|
"vite": "^5.0.0",
|
|
106
107
|
"vite-plugin-dts": "^3.6.0",
|
|
107
108
|
"vitest": "^1.0.0",
|
|
108
|
-
"@isi-ui7/
|
|
109
|
+
"@isi-ui7/editable-table": "0.2.3",
|
|
110
|
+
"@isi-ui7/lookup-input": "0.2.4",
|
|
109
111
|
"@isi-ui7/i18n": "0.2.3",
|
|
110
|
-
"@isi-ui7/modal-manager": "0.2.2",
|
|
111
|
-
"@isi-ui7/realtime": "0.2.3",
|
|
112
|
-
"@isi-ui7/ui-shell": "0.2.8",
|
|
113
112
|
"@isi-ui7/data-table": "0.2.4",
|
|
114
|
-
"@isi-ui7/
|
|
115
|
-
"@isi-ui7/
|
|
113
|
+
"@isi-ui7/corporate-themes": "0.2.6",
|
|
114
|
+
"@isi-ui7/realtime": "0.2.3",
|
|
115
|
+
"@isi-ui7/modal-manager": "0.2.2",
|
|
116
|
+
"@isi-ui7/ui-shell": "0.2.8"
|
|
116
117
|
},
|
|
117
118
|
"dependencies": {
|
|
118
119
|
"@carbon/icons-react": "^11.71.0",
|
|
@@ -125,9 +126,9 @@
|
|
|
125
126
|
"jose": "^5.9.6",
|
|
126
127
|
"jspdf": "^4.2.1",
|
|
127
128
|
"jspdf-autotable": "^5.0.7",
|
|
128
|
-
"@isi-ui7/
|
|
129
|
+
"@isi-ui7/data-table": "0.2.4",
|
|
129
130
|
"@isi-ui7/editable-table": "0.2.3",
|
|
130
|
-
"@isi-ui7/
|
|
131
|
+
"@isi-ui7/lookup-input": "0.2.4"
|
|
131
132
|
},
|
|
132
133
|
"scripts": {
|
|
133
134
|
"build": "vite build",
|
package/src/crud-components.tsx
CHANGED
|
@@ -12,6 +12,9 @@ import {
|
|
|
12
12
|
ModalHeader,
|
|
13
13
|
ProgressIndicator,
|
|
14
14
|
ProgressStep,
|
|
15
|
+
Select,
|
|
16
|
+
SelectItem,
|
|
17
|
+
TextArea,
|
|
15
18
|
ToastNotification,
|
|
16
19
|
Toggle,
|
|
17
20
|
} from "@carbon/react";
|
|
@@ -71,9 +74,19 @@ export function SharedCrudListPage({
|
|
|
71
74
|
// deactivated / soft-deleted rows. Survives table remounts (tableKey).
|
|
72
75
|
const [showInactive, setShowInactive] = useState(false);
|
|
73
76
|
const inactiveParam = schema.includeInactiveParam ?? "include_inactive";
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
// Single-select toolbar filter (opt-in via schema.filterSelect) — same
|
|
78
|
+
// additionalParams wiring as showInactiveToggle, for an arbitrary enum
|
|
79
|
+
// column instead of a boolean.
|
|
80
|
+
const [filterValue, setFilterValue] = useState("");
|
|
81
|
+
const hasInactiveToggle = Boolean(schema.showInactiveToggle);
|
|
82
|
+
const hasFilterSelect = Boolean(schema.filterSelect);
|
|
83
|
+
const additionalParams =
|
|
84
|
+
hasInactiveToggle || hasFilterSelect
|
|
85
|
+
? {
|
|
86
|
+
...(hasInactiveToggle ? { [inactiveParam]: showInactive ? "true" : "" } : {}),
|
|
87
|
+
...(hasFilterSelect ? { [schema.filterSelect!.param]: filterValue } : {}),
|
|
88
|
+
}
|
|
89
|
+
: undefined;
|
|
77
90
|
return (
|
|
78
91
|
<PageLayout>
|
|
79
92
|
<PageHeader title={schema.title} subtitle={schema.subtitle} />
|
|
@@ -104,6 +117,23 @@ export function SharedCrudListPage({
|
|
|
104
117
|
onToggle={(checked) => setShowInactive(checked)}
|
|
105
118
|
/>
|
|
106
119
|
) : null}
|
|
120
|
+
{schema.filterSelect ? (
|
|
121
|
+
<div style={{ minWidth: "10rem" }}>
|
|
122
|
+
<Select
|
|
123
|
+
id={`filter-select-${schema.apiPath}`}
|
|
124
|
+
labelText=""
|
|
125
|
+
hideLabel
|
|
126
|
+
size="sm"
|
|
127
|
+
value={filterValue}
|
|
128
|
+
onChange={(e) => setFilterValue(e.target.value)}
|
|
129
|
+
>
|
|
130
|
+
<SelectItem value="" text={schema.filterSelect.allLabel ?? "Semua"} />
|
|
131
|
+
{schema.filterSelect.options.map((opt) => (
|
|
132
|
+
<SelectItem key={opt.value} value={opt.value} text={opt.label} />
|
|
133
|
+
))}
|
|
134
|
+
</Select>
|
|
135
|
+
</div>
|
|
136
|
+
) : null}
|
|
107
137
|
{toolbarExtra}
|
|
108
138
|
<Button renderIcon={Add} size="sm" onClick={onAdd}>
|
|
109
139
|
{schema.addButtonLabel || labels.next}
|
|
@@ -154,6 +184,7 @@ export function CrudSchemaListPage<TData extends Record<string, unknown>>({
|
|
|
154
184
|
const [customActionRow, setCustomActionRow] = useState<Record<string, unknown> | null>(null);
|
|
155
185
|
const [customActioning, setCustomActioning] = useState(false);
|
|
156
186
|
const [customActionError, setCustomActionError] = useState<string | null>(null);
|
|
187
|
+
const [customActionReason, setCustomActionReason] = useState("");
|
|
157
188
|
|
|
158
189
|
// Workflow completion toast
|
|
159
190
|
const [wfToast, setWfToast] = useState<{ kind: 'success' | 'error'; title: string; subtitle?: string } | null>(null);
|
|
@@ -192,6 +223,7 @@ export function CrudSchemaListPage<TData extends Record<string, unknown>>({
|
|
|
192
223
|
setCustomActionId(actionId);
|
|
193
224
|
setCustomActionRow(row);
|
|
194
225
|
setCustomActionError(null);
|
|
226
|
+
setCustomActionReason("");
|
|
195
227
|
return;
|
|
196
228
|
}
|
|
197
229
|
|
|
@@ -224,6 +256,7 @@ export function CrudSchemaListPage<TData extends Record<string, unknown>>({
|
|
|
224
256
|
if (!schema) return;
|
|
225
257
|
const id = String(customActionRow.id ?? "");
|
|
226
258
|
const path = typeof schema.apiPath === "function" ? schema.apiPath(id) : schema.apiPath;
|
|
259
|
+
const body = schema.reasonRequired ? { reason: customActionReason.trim() } : {};
|
|
227
260
|
setCustomActioning(true);
|
|
228
261
|
setCustomActionError(null);
|
|
229
262
|
try {
|
|
@@ -232,17 +265,17 @@ export function CrudSchemaListPage<TData extends Record<string, unknown>>({
|
|
|
232
265
|
const res = await fetch(path, {
|
|
233
266
|
method: schema.method ?? "POST",
|
|
234
267
|
headers: { "Content-Type": "application/json" },
|
|
235
|
-
body: JSON.stringify(
|
|
268
|
+
body: JSON.stringify(body),
|
|
236
269
|
});
|
|
237
270
|
if (!res.ok) {
|
|
238
|
-
const
|
|
239
|
-
throw new Error(
|
|
271
|
+
const resBody = (await res.json().catch(() => ({}))) as { error?: string };
|
|
272
|
+
throw new Error(resBody.error ?? `Gagal (HTTP ${res.status})`);
|
|
240
273
|
}
|
|
241
274
|
setCustomActionId(null);
|
|
242
275
|
setCustomActionRow(null);
|
|
243
276
|
setTableKey((v) => v + 1);
|
|
244
277
|
} else {
|
|
245
|
-
const instanceId = await submitWorkflowForm(path, schema.method ?? "POST",
|
|
278
|
+
const instanceId = await submitWorkflowForm(path, schema.method ?? "POST", body);
|
|
246
279
|
setCustomActionId(null);
|
|
247
280
|
setCustomActionRow(null);
|
|
248
281
|
setTableKey((v) => v + 1);
|
|
@@ -253,7 +286,7 @@ export function CrudSchemaListPage<TData extends Record<string, unknown>>({
|
|
|
253
286
|
} finally {
|
|
254
287
|
setCustomActioning(false);
|
|
255
288
|
}
|
|
256
|
-
}, [customActionId, customActionRow, listSchema.customActionSchemas, tracker]);
|
|
289
|
+
}, [customActionId, customActionRow, customActionReason, listSchema.customActionSchemas, tracker]);
|
|
257
290
|
|
|
258
291
|
const activeCustomSchema =
|
|
259
292
|
customActionId ? listSchema.customActionSchemas?.[customActionId] : undefined;
|
|
@@ -262,6 +295,7 @@ export function CrudSchemaListPage<TData extends Record<string, unknown>>({
|
|
|
262
295
|
? activeCustomSchema.description(customActionRow ?? {})
|
|
263
296
|
: activeCustomSchema.description)
|
|
264
297
|
: "";
|
|
298
|
+
const customReasonMissing = Boolean(activeCustomSchema?.reasonRequired) && customActionReason.trim().length === 0;
|
|
265
299
|
|
|
266
300
|
return (
|
|
267
301
|
<>
|
|
@@ -308,6 +342,15 @@ export function CrudSchemaListPage<TData extends Record<string, unknown>>({
|
|
|
308
342
|
<ModalBody hasScrollingContent={false}>
|
|
309
343
|
<div style={{ display: "grid", gap: "0.5rem", fontSize: "0.875rem" }}>
|
|
310
344
|
<p>{customDescription}</p>
|
|
345
|
+
{activeCustomSchema.reasonRequired ? (
|
|
346
|
+
<TextArea
|
|
347
|
+
labelText={activeCustomSchema.reasonLabel ?? "Alasan"}
|
|
348
|
+
value={customActionReason}
|
|
349
|
+
onChange={(e) => setCustomActionReason(e.target.value)}
|
|
350
|
+
disabled={customActioning}
|
|
351
|
+
rows={3}
|
|
352
|
+
/>
|
|
353
|
+
) : null}
|
|
311
354
|
{customActionError ? (
|
|
312
355
|
<InlineNotification
|
|
313
356
|
kind="error"
|
|
@@ -329,7 +372,7 @@ export function CrudSchemaListPage<TData extends Record<string, unknown>>({
|
|
|
329
372
|
<Button
|
|
330
373
|
kind="primary"
|
|
331
374
|
onClick={() => void doCustomAction()}
|
|
332
|
-
disabled={customActioning || !customActionRow}
|
|
375
|
+
disabled={customActioning || !customActionRow || customReasonMissing}
|
|
333
376
|
>
|
|
334
377
|
{customActioning ? labels.saving : (activeCustomSchema.confirmLabel ?? labels.next)}
|
|
335
378
|
</Button>
|
package/src/crud-types.ts
CHANGED
|
@@ -37,6 +37,16 @@ export type CrudCustomActionSchema = {
|
|
|
37
37
|
* Dipakai untuk aksi operasional seperti employee terminate/reactivate.
|
|
38
38
|
*/
|
|
39
39
|
direct?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Tampilkan textarea alasan di modal konfirmasi dan wajibkan isi non-kosong
|
|
42
|
+
* sebelum tombol confirm aktif. Nilai dikirim sebagai `{ reason }` di body
|
|
43
|
+
* request (digabung ke payload lain bila apiPath sama dipakai untuk field
|
|
44
|
+
* lain). Dipakai untuk aksi yang butuh audit trail eksplisit (mis. Set
|
|
45
|
+
* Non-Aktif produk) — lihat appdefs/{module}/index.def `withReason: true`.
|
|
46
|
+
*/
|
|
47
|
+
reasonRequired?: boolean;
|
|
48
|
+
/** Label di atas textarea alasan. Default "Alasan". */
|
|
49
|
+
reasonLabel?: string;
|
|
40
50
|
};
|
|
41
51
|
|
|
42
52
|
export type CrudListSchema = {
|
|
@@ -76,6 +86,22 @@ export type CrudListSchema = {
|
|
|
76
86
|
* or onCustomAction).
|
|
77
87
|
*/
|
|
78
88
|
popupMenuItemsForRow?: (row: Record<string, unknown>) => CrudPopupMenuItem[];
|
|
89
|
+
/**
|
|
90
|
+
* Optional single-select toolbar filter. When an option other than "(all)"
|
|
91
|
+
* is picked, the list request carries `?<param>=<value>`; picking back to
|
|
92
|
+
* "(all)" removes the param. Mirrors `showInactiveToggle`'s additionalParams
|
|
93
|
+
* wiring but for an arbitrary enum column (e.g. `vendor_type`). Additive +
|
|
94
|
+
* backward-compatible: omit it and the toolbar renders exactly as before.
|
|
95
|
+
*/
|
|
96
|
+
filterSelect?: {
|
|
97
|
+
/** Query param name the backend reads (e.g. "vendor_type"). */
|
|
98
|
+
param: string;
|
|
99
|
+
/** Toolbar label for the select. */
|
|
100
|
+
label: string;
|
|
101
|
+
options: Array<{ value: string; label: string }>;
|
|
102
|
+
/** Label for the "no filter" option. Default "Semua". */
|
|
103
|
+
allLabel?: string;
|
|
104
|
+
};
|
|
79
105
|
};
|
|
80
106
|
|
|
81
107
|
export type CrudDeleteSchema<TData extends Record<string, unknown>> = {
|
package/src/data-table/proxy.ts
CHANGED
|
@@ -10,6 +10,15 @@ export interface ProxyBackendPostOptions {
|
|
|
10
10
|
accessTokenCookie?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// Toolbar filters (showInactiveToggle, filterSelect) ride as query-string
|
|
14
|
+
// params on the incoming BFF request (see ServerDataTable's additionalParams)
|
|
15
|
+
// even though the body carries the DataTable sort/search/pagination payload.
|
|
16
|
+
// Forward that query string to the backend or those filters silently no-op.
|
|
17
|
+
function backendPathWithQuery(req: NextRequest, path: string): string {
|
|
18
|
+
const search = new URL(req.url).search;
|
|
19
|
+
return search ? `${path}${search}` : path;
|
|
20
|
+
}
|
|
21
|
+
|
|
13
22
|
export async function proxyBackendPost(
|
|
14
23
|
req: NextRequest,
|
|
15
24
|
opts: ProxyBackendPostOptions,
|
|
@@ -17,7 +26,7 @@ export async function proxyBackendPost(
|
|
|
17
26
|
const accessToken = req.cookies.get(opts.accessTokenCookie ?? "access_token")?.value;
|
|
18
27
|
const result = await exchangeUserToken(accessToken ?? "", opts.audience);
|
|
19
28
|
const body = await req.json();
|
|
20
|
-
const res = await fetch(`${opts.backendUrl}${opts.path}`, {
|
|
29
|
+
const res = await fetch(`${opts.backendUrl}${backendPathWithQuery(req, opts.path)}`, {
|
|
21
30
|
method: "POST",
|
|
22
31
|
headers: {
|
|
23
32
|
"Content-Type": "application/json",
|
|
@@ -57,7 +66,7 @@ export async function proxyQueryRoute(
|
|
|
57
66
|
const accessToken = req.cookies.get(opts.accessTokenCookie ?? "access_token")?.value;
|
|
58
67
|
const result = await exchangeUserToken(accessToken ?? "", opts.audience);
|
|
59
68
|
const body = await req.json();
|
|
60
|
-
const res = await fetch(`${opts.backendUrl}${opts.path}`, {
|
|
69
|
+
const res = await fetch(`${opts.backendUrl}${backendPathWithQuery(req, opts.path)}`, {
|
|
61
70
|
method: "POST",
|
|
62
71
|
headers: {
|
|
63
72
|
"Content-Type": "application/json",
|