@duke333/builder-ui 0.1.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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/RuntimeEntryDialog-DAO74GFp.d.ts +282 -0
- package/dist/admin/index.d.ts +631 -0
- package/dist/admin/index.js +644 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/api/index.d.ts +99 -0
- package/dist/api/index.js +92 -0
- package/dist/api/index.js.map +1 -0
- package/dist/components/index.d.ts +137 -0
- package/dist/components/index.js +581 -0
- package/dist/components/index.js.map +1 -0
- package/dist/hooks/index.d.ts +107 -0
- package/dist/hooks/index.js +301 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +1397 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/index.d.ts +28 -0
- package/dist/runtime/index.js +31 -0
- package/dist/runtime/index.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
// src/components/DynamicForm.tsx
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { FormProvider, useForm } from "react-hook-form";
|
|
4
|
+
|
|
5
|
+
// src/runtime/runtimeUtils.ts
|
|
6
|
+
function emptyFieldValue(field) {
|
|
7
|
+
return field.type === "checkbox" ? false : "";
|
|
8
|
+
}
|
|
9
|
+
function createBuilderFormDefaultValues(schema) {
|
|
10
|
+
const values = {};
|
|
11
|
+
for (const field of schema?.fields ?? []) {
|
|
12
|
+
values[field.key] = emptyFieldValue(field);
|
|
13
|
+
}
|
|
14
|
+
return { values };
|
|
15
|
+
}
|
|
16
|
+
function createBuilderEntryDefaultValues(view, editing) {
|
|
17
|
+
const values = {};
|
|
18
|
+
for (const field of view.form?.schema.fields ?? []) {
|
|
19
|
+
const existing = editing?.values[field.key];
|
|
20
|
+
values[field.key] = existing === void 0 ? emptyFieldValue(field) : existing;
|
|
21
|
+
}
|
|
22
|
+
return { values, version: editing?.version ?? 0 };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/components/DynamicForm.tsx
|
|
26
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
27
|
+
function createDynamicFormComponent(deps) {
|
|
28
|
+
return function DynamicForm({ formId }) {
|
|
29
|
+
const query = deps.hooks.useRuntimeBuilderForm(formId);
|
|
30
|
+
const save = deps.hooks.useSaveRuntimeBuilderFormEntry(formId);
|
|
31
|
+
const formValues = useMemo(
|
|
32
|
+
() => query.data ? createBuilderFormDefaultValues(query.data.schema) : void 0,
|
|
33
|
+
[query.data]
|
|
34
|
+
);
|
|
35
|
+
const form = useForm({
|
|
36
|
+
defaultValues: createBuilderFormDefaultValues(query.data?.schema),
|
|
37
|
+
values: formValues
|
|
38
|
+
});
|
|
39
|
+
const onSubmit = form.handleSubmit(async (values) => {
|
|
40
|
+
try {
|
|
41
|
+
await save.mutateAsync(values);
|
|
42
|
+
form.reset(createBuilderFormDefaultValues(query.data?.schema));
|
|
43
|
+
deps.notifications.success("Form \u0445\u0430\u0434\u0433\u0430\u043B\u0430\u0433\u0434\u043B\u0430\u0430");
|
|
44
|
+
} catch (error) {
|
|
45
|
+
deps.notifications.error(deps.notifications.errorMessage(error, "\u0425\u0430\u0434\u0433\u0430\u043B\u0430\u0445\u0430\u0434 \u0430\u043B\u0434\u0430\u0430 \u0433\u0430\u0440\u043B\u0430\u0430"));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
if (query.isError) {
|
|
49
|
+
return /* @__PURE__ */ jsx(
|
|
50
|
+
deps.ErrorState,
|
|
51
|
+
{
|
|
52
|
+
title: "Form \u043D\u044D\u044D\u0433\u0434\u0441\u044D\u043D\u0433\u04AF\u0439",
|
|
53
|
+
description: `\xAB${formId}\xBB id-\u0442\u0430\u0439 form \u043E\u043B\u0434\u0441\u043E\u043D\u0433\u04AF\u0439.`
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (!query.data) return null;
|
|
58
|
+
return /* @__PURE__ */ jsx(deps.FormShell, { title: query.data.title, children: /* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit, noValidate: true, className: "space-y-6", children: [
|
|
59
|
+
/* @__PURE__ */ jsx(deps.FormFields, { formId, schema: query.data.schema, namePrefix: "values" }),
|
|
60
|
+
/* @__PURE__ */ jsx(deps.SubmitButton, { isSaving: save.isPending })
|
|
61
|
+
] }) }) });
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/components/DynamicList.tsx
|
|
66
|
+
import { useMemo as useMemo2 } from "react";
|
|
67
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
68
|
+
function createDynamicListComponent(deps) {
|
|
69
|
+
return function DynamicList({ listId }) {
|
|
70
|
+
const query = deps.hooks.useRuntimeBuilderList(listId);
|
|
71
|
+
const state = deps.useDataTableState({ pageSize: 20 });
|
|
72
|
+
const entriesQuery = deps.hooks.useRuntimeBuilderListEntries(listId, state.query);
|
|
73
|
+
const columns = useMemo2(() => query.data ? deps.buildColumns(query.data) : [], [query.data]);
|
|
74
|
+
const entries = entriesQuery.data?.items ?? [];
|
|
75
|
+
const controller = deps.useDataTable({
|
|
76
|
+
columns,
|
|
77
|
+
data: entries,
|
|
78
|
+
rowCount: entriesQuery.data?.total ?? 0,
|
|
79
|
+
state,
|
|
80
|
+
getRowId: (row) => row.id
|
|
81
|
+
});
|
|
82
|
+
if (query.isError) {
|
|
83
|
+
return /* @__PURE__ */ jsx2(
|
|
84
|
+
deps.ErrorState,
|
|
85
|
+
{
|
|
86
|
+
title: "List \u043D\u044D\u044D\u0433\u0434\u0441\u044D\u043D\u0433\u04AF\u0439",
|
|
87
|
+
description: `\xAB${listId}\xBB id-\u0442\u0430\u0439 list \u043E\u043B\u0434\u0441\u043E\u043D\u0433\u04AF\u0439.`
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return /* @__PURE__ */ jsx2(
|
|
92
|
+
deps.DataGrid,
|
|
93
|
+
{
|
|
94
|
+
title: query.data?.title ?? "Dynamic List",
|
|
95
|
+
description: query.data?.description ?? `/winu/list?id=${listId}`,
|
|
96
|
+
controller,
|
|
97
|
+
isLoading: query.isPending || entriesQuery.isPending,
|
|
98
|
+
searchPlaceholder: "\u041C\u04E9\u0440 \u0445\u0430\u0439\u0445...",
|
|
99
|
+
emptyTitle: "\u041C\u04E9\u0440 \u0430\u043B\u0433\u0430",
|
|
100
|
+
emptyDescription: "\u042D\u043D\u044D list-\u0438\u0439\u043D datasource \u0434\u044D\u044D\u0440 \u043C\u04E9\u0440 \u0430\u043B\u0433\u0430."
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/components/DynamicView.tsx
|
|
107
|
+
import { useMemo as useMemo3, useState } from "react";
|
|
108
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
109
|
+
function createDynamicViewComponent(deps) {
|
|
110
|
+
return function DynamicView({ viewId }) {
|
|
111
|
+
const [editing, setEditing] = useState(null);
|
|
112
|
+
const [isDialogOpen, setDialogOpen] = useState(false);
|
|
113
|
+
const [pendingDelete, setPendingDelete] = useState(null);
|
|
114
|
+
const viewQuery = deps.hooks.useRuntimeBuilderView(viewId);
|
|
115
|
+
const state = deps.useDataTableState({ pageSize: 20 });
|
|
116
|
+
const entriesQuery = deps.hooks.useBuilderViewEntries(viewId, state.query);
|
|
117
|
+
const save = deps.hooks.useSaveBuilderViewEntry(viewId);
|
|
118
|
+
const remove = deps.hooks.useDeleteBuilderViewEntry(viewId);
|
|
119
|
+
const view = viewQuery.data;
|
|
120
|
+
const columns = useMemo3(
|
|
121
|
+
() => view ? deps.buildColumns(view, {
|
|
122
|
+
onEdit: (entry) => {
|
|
123
|
+
setEditing(entry);
|
|
124
|
+
setDialogOpen(true);
|
|
125
|
+
},
|
|
126
|
+
onDelete: setPendingDelete
|
|
127
|
+
}) : [],
|
|
128
|
+
[view]
|
|
129
|
+
);
|
|
130
|
+
const controller = deps.useDataTable({
|
|
131
|
+
columns,
|
|
132
|
+
data: entriesQuery.data?.items ?? [],
|
|
133
|
+
rowCount: entriesQuery.data?.total ?? 0,
|
|
134
|
+
state,
|
|
135
|
+
getRowId: (row) => row.id
|
|
136
|
+
});
|
|
137
|
+
if (viewQuery.isError) {
|
|
138
|
+
return /* @__PURE__ */ jsx3(
|
|
139
|
+
deps.ErrorState,
|
|
140
|
+
{
|
|
141
|
+
title: "View \u043D\u044D\u044D\u0433\u0434\u0441\u044D\u043D\u0433\u04AF\u0439",
|
|
142
|
+
description: `\xAB${viewId}\xBB id-\u0442\u0430\u0439 view \u043E\u043B\u0434\u0441\u043E\u043D\u0433\u04AF\u0439.`
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return /* @__PURE__ */ jsxs2("div", { className: "space-y-6", children: [
|
|
147
|
+
/* @__PURE__ */ jsx3(
|
|
148
|
+
deps.DataGrid,
|
|
149
|
+
{
|
|
150
|
+
title: view?.title ?? "Dynamic View",
|
|
151
|
+
description: view?.description ?? `/winu/view?id=${viewId}`,
|
|
152
|
+
controller,
|
|
153
|
+
isLoading: viewQuery.isPending || entriesQuery.isPending,
|
|
154
|
+
searchPlaceholder: "\u041C\u04E9\u0440 \u0445\u0430\u0439\u0445...",
|
|
155
|
+
emptyTitle: "\u041C\u04E9\u0440 \u0430\u043B\u0433\u0430",
|
|
156
|
+
emptyDescription: "\u042D\u043D\u044D view \u0434\u044D\u044D\u0440 \u044D\u0445\u043D\u0438\u0439 \u043C\u04E9\u0440\u04E9\u04E9 \u043D\u044D\u043C\u044D\u044D\u0440\u044D\u0439.",
|
|
157
|
+
onRowClick: (entry) => {
|
|
158
|
+
setEditing(entry);
|
|
159
|
+
setDialogOpen(true);
|
|
160
|
+
},
|
|
161
|
+
toolbarExtra: /* @__PURE__ */ jsx3(
|
|
162
|
+
deps.AddButton,
|
|
163
|
+
{
|
|
164
|
+
disabled: !view?.form,
|
|
165
|
+
onAdd: () => {
|
|
166
|
+
setEditing(null);
|
|
167
|
+
setDialogOpen(true);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
view ? /* @__PURE__ */ jsx3(
|
|
174
|
+
deps.EntryDialog,
|
|
175
|
+
{
|
|
176
|
+
open: isDialogOpen,
|
|
177
|
+
view,
|
|
178
|
+
editing,
|
|
179
|
+
onClose: () => setDialogOpen(false),
|
|
180
|
+
onSave: save.mutateAsync,
|
|
181
|
+
isSaving: save.isPending
|
|
182
|
+
}
|
|
183
|
+
) : null,
|
|
184
|
+
/* @__PURE__ */ jsx3(
|
|
185
|
+
deps.DeleteDialog,
|
|
186
|
+
{
|
|
187
|
+
entry: pendingDelete,
|
|
188
|
+
isDeleting: remove.isPending,
|
|
189
|
+
onClose: () => setPendingDelete(null),
|
|
190
|
+
onConfirm: async () => {
|
|
191
|
+
if (!pendingDelete) return;
|
|
192
|
+
await remove.mutateAsync(pendingDelete.id);
|
|
193
|
+
setPendingDelete(null);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
] });
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/components/RuntimeEntryDialog.tsx
|
|
202
|
+
import { useEffect, useMemo as useMemo4, useState as useState2 } from "react";
|
|
203
|
+
import { FormProvider as FormProvider2, useForm as useForm2 } from "react-hook-form";
|
|
204
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
205
|
+
function isDbTableSource(source) {
|
|
206
|
+
return source?.type === "dbTable" && "tableName" in source;
|
|
207
|
+
}
|
|
208
|
+
function runtimeViewTableName(view) {
|
|
209
|
+
const listSource = view.list?.source;
|
|
210
|
+
if (isDbTableSource(listSource)) return listSource.tableName;
|
|
211
|
+
const formSource = view.form?.source;
|
|
212
|
+
return isDbTableSource(formSource) ? formSource.tableName : void 0;
|
|
213
|
+
}
|
|
214
|
+
function cleanRowValues(values) {
|
|
215
|
+
return Object.fromEntries(
|
|
216
|
+
Object.entries(values).filter(([key]) => key === "_delete" || !key.startsWith("_"))
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
function childRowPayload(row, includeIdentity) {
|
|
220
|
+
const values = cleanRowValues(row.values);
|
|
221
|
+
if (!includeIdentity || row.values._draftNew === true) return values;
|
|
222
|
+
return {
|
|
223
|
+
id: row.id,
|
|
224
|
+
version: row.version,
|
|
225
|
+
...values
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function createRuntimeEntryDialogComponent(deps) {
|
|
229
|
+
return function RuntimeEntryDialog({
|
|
230
|
+
open,
|
|
231
|
+
view,
|
|
232
|
+
editing,
|
|
233
|
+
onClose,
|
|
234
|
+
onSave,
|
|
235
|
+
isSaving
|
|
236
|
+
}) {
|
|
237
|
+
const [draftSubforms, setDraftSubforms] = useState2({});
|
|
238
|
+
const compositeCreate = deps.useCreateCompositeEntry?.(view.id);
|
|
239
|
+
const compositeUpdate = deps.useUpdateCompositeEntry?.(view.id);
|
|
240
|
+
const form = useForm2({
|
|
241
|
+
defaultValues: createBuilderEntryDefaultValues(view, editing),
|
|
242
|
+
values: open ? createBuilderEntryDefaultValues(view, editing) : void 0
|
|
243
|
+
});
|
|
244
|
+
const mainTableName = runtimeViewTableName(view);
|
|
245
|
+
const hasDraftRows = useMemo4(
|
|
246
|
+
() => Object.values(draftSubforms).some((draft) => draft.rows.length > 0),
|
|
247
|
+
[draftSubforms]
|
|
248
|
+
);
|
|
249
|
+
useEffect(() => {
|
|
250
|
+
if (open) setDraftSubforms({});
|
|
251
|
+
}, [open]);
|
|
252
|
+
const onSubmit = form.handleSubmit(async (values) => {
|
|
253
|
+
try {
|
|
254
|
+
if (!editing && hasDraftRows) {
|
|
255
|
+
if (!mainTableName) throw new Error("Main form datasource table \u043E\u043B\u0434\u0441\u043E\u043D\u0433\u04AF\u0439");
|
|
256
|
+
if (!compositeCreate) throw new Error("Composite create handler \u0442\u043E\u0445\u0438\u0440\u0443\u0443\u043B\u0430\u0433\u0434\u0430\u0430\u0433\u04AF\u0439 \u0431\u0430\u0439\u043D\u0430");
|
|
257
|
+
await compositeCreate.mutateAsync({
|
|
258
|
+
data: {
|
|
259
|
+
[mainTableName]: values.values,
|
|
260
|
+
...Object.fromEntries(
|
|
261
|
+
Object.values(draftSubforms).map((draft) => [
|
|
262
|
+
draft.dataKey,
|
|
263
|
+
draft.rows.filter((row) => row.values._delete !== true).map((row) => childRowPayload(row, false))
|
|
264
|
+
])
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
} else if (editing && hasDraftRows) {
|
|
269
|
+
if (!mainTableName) throw new Error("Main form datasource table \u043E\u043B\u0434\u0441\u043E\u043D\u0433\u04AF\u0439");
|
|
270
|
+
if (!compositeUpdate) throw new Error("Composite update handler \u0442\u043E\u0445\u0438\u0440\u0443\u0443\u043B\u0430\u0433\u0434\u0430\u0430\u0433\u04AF\u0439 \u0431\u0430\u0439\u043D\u0430");
|
|
271
|
+
await compositeUpdate.mutateAsync({
|
|
272
|
+
id: editing.id,
|
|
273
|
+
input: {
|
|
274
|
+
data: {
|
|
275
|
+
[mainTableName]: {
|
|
276
|
+
id: editing.id,
|
|
277
|
+
version: values.version,
|
|
278
|
+
...values.values
|
|
279
|
+
},
|
|
280
|
+
...Object.fromEntries(
|
|
281
|
+
Object.values(draftSubforms).map((draft) => [
|
|
282
|
+
draft.dataKey,
|
|
283
|
+
draft.rows.map((row) => childRowPayload(row, true))
|
|
284
|
+
])
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
} else {
|
|
290
|
+
await onSave({ id: editing?.id, input: values });
|
|
291
|
+
}
|
|
292
|
+
onClose();
|
|
293
|
+
deps.notifications.success(editing ? "\u041C\u04E9\u0440 \u0445\u0430\u0434\u0433\u0430\u043B\u0430\u0433\u0434\u043B\u0430\u0430" : "\u041C\u04E9\u0440 \u043D\u044D\u043C\u044D\u0433\u0434\u043B\u044D\u044D");
|
|
294
|
+
} catch (error) {
|
|
295
|
+
deps.notifications.error(deps.notifications.errorMessage(error, "\u0425\u0430\u0434\u0433\u0430\u043B\u0430\u0445\u0430\u0434 \u0430\u043B\u0434\u0430\u0430 \u0433\u0430\u0440\u043B\u0430\u0430"));
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
const saving = isSaving || Boolean(compositeCreate?.isPending || compositeUpdate?.isPending);
|
|
299
|
+
const extraItems = deps.SubformField && view.form?.schema.subforms?.length ? view.form.schema.subforms.map((subform) => ({
|
|
300
|
+
key: subform.key,
|
|
301
|
+
render: () => deps.SubformField ? /* @__PURE__ */ jsx4(
|
|
302
|
+
deps.SubformField,
|
|
303
|
+
{
|
|
304
|
+
subform,
|
|
305
|
+
parentEntry: editing,
|
|
306
|
+
draftRows: draftSubforms[subform.key]?.rows ?? [],
|
|
307
|
+
onDraftRowsChange: ({ subformKey, dataKey, rows }) => {
|
|
308
|
+
setDraftSubforms((current) => ({
|
|
309
|
+
...current,
|
|
310
|
+
[subformKey]: { dataKey, rows }
|
|
311
|
+
}));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
) : null
|
|
315
|
+
})) : void 0;
|
|
316
|
+
return /* @__PURE__ */ jsx4(
|
|
317
|
+
deps.DialogShell,
|
|
318
|
+
{
|
|
319
|
+
open,
|
|
320
|
+
title: editing ? "\u041C\u04E9\u0440 \u0437\u0430\u0441\u0430\u0445" : "\u041C\u04E9\u0440 \u043D\u044D\u043C\u044D\u0445",
|
|
321
|
+
description: view.title,
|
|
322
|
+
onClose,
|
|
323
|
+
children: /* @__PURE__ */ jsx4(FormProvider2, { ...form, children: /* @__PURE__ */ jsxs3("form", { onSubmit, noValidate: true, className: deps.formClassName, children: [
|
|
324
|
+
view.form ? /* @__PURE__ */ jsx4(
|
|
325
|
+
deps.FormFields,
|
|
326
|
+
{
|
|
327
|
+
formId: view.form.id,
|
|
328
|
+
viewId: view.id,
|
|
329
|
+
entry: editing,
|
|
330
|
+
schema: view.form.schema,
|
|
331
|
+
namePrefix: "values",
|
|
332
|
+
extraItems
|
|
333
|
+
}
|
|
334
|
+
) : null,
|
|
335
|
+
/* @__PURE__ */ jsx4(deps.Actions, { isSaving: saving, onClose })
|
|
336
|
+
] }) })
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/components/RuntimeSubformField.tsx
|
|
343
|
+
import { useEffect as useEffect2, useMemo as useMemo5, useRef, useState as useState3 } from "react";
|
|
344
|
+
import { FormProvider as FormProvider3, useForm as useForm3 } from "react-hook-form";
|
|
345
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
346
|
+
function subformButtonLabel(subform) {
|
|
347
|
+
return subform.buttonLabel?.trim() || subform.label;
|
|
348
|
+
}
|
|
349
|
+
function parentValue(entry, field) {
|
|
350
|
+
if (!entry) return void 0;
|
|
351
|
+
const value = field === "id" ? entry.id : entry.values[field];
|
|
352
|
+
if (typeof value === "string") return value;
|
|
353
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
354
|
+
return void 0;
|
|
355
|
+
}
|
|
356
|
+
function isDbTableSource2(source) {
|
|
357
|
+
return source?.type === "dbTable" && "tableName" in source;
|
|
358
|
+
}
|
|
359
|
+
function subformFallbackKey(view) {
|
|
360
|
+
return view.id;
|
|
361
|
+
}
|
|
362
|
+
function viewTableName(view) {
|
|
363
|
+
const source = view.list?.source;
|
|
364
|
+
return isDbTableSource2(source) ? source.tableName : subformFallbackKey(view);
|
|
365
|
+
}
|
|
366
|
+
function draftEntry(values, editing) {
|
|
367
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
368
|
+
return {
|
|
369
|
+
id: editing?.id ?? crypto.randomUUID(),
|
|
370
|
+
viewId: editing?.viewId ?? "",
|
|
371
|
+
values: editing ? values.values : { ...values.values, _draftNew: true },
|
|
372
|
+
version: editing?.version ?? 0,
|
|
373
|
+
createdAt: editing?.createdAt ?? now,
|
|
374
|
+
updatedAt: now
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function RuntimeSubformEntryDialog({
|
|
378
|
+
open,
|
|
379
|
+
title,
|
|
380
|
+
view,
|
|
381
|
+
childField,
|
|
382
|
+
parentValue: linkedParentValue,
|
|
383
|
+
editing,
|
|
384
|
+
isSaving,
|
|
385
|
+
deps,
|
|
386
|
+
onClose,
|
|
387
|
+
onSave
|
|
388
|
+
}) {
|
|
389
|
+
const defaultValues = useMemo5(() => {
|
|
390
|
+
const values = createBuilderEntryDefaultValues(view, editing);
|
|
391
|
+
if (linkedParentValue) {
|
|
392
|
+
values.values[childField] = editing?.values[childField] ?? linkedParentValue;
|
|
393
|
+
}
|
|
394
|
+
return values;
|
|
395
|
+
}, [childField, editing, linkedParentValue, view]);
|
|
396
|
+
const form = useForm3({
|
|
397
|
+
defaultValues,
|
|
398
|
+
values: open ? defaultValues : void 0
|
|
399
|
+
});
|
|
400
|
+
const onSubmit = form.handleSubmit(async (values) => {
|
|
401
|
+
try {
|
|
402
|
+
await onSave({
|
|
403
|
+
id: editing?.id,
|
|
404
|
+
input: {
|
|
405
|
+
...values,
|
|
406
|
+
values: {
|
|
407
|
+
...values.values,
|
|
408
|
+
...linkedParentValue ? { [childField]: linkedParentValue } : {}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
onClose();
|
|
413
|
+
deps.notifications.success(editing ? "Section \u0445\u0430\u0434\u0433\u0430\u043B\u0430\u0433\u0434\u043B\u0430\u0430" : "Section \u043D\u044D\u043C\u044D\u0433\u0434\u043B\u044D\u044D");
|
|
414
|
+
} catch (error) {
|
|
415
|
+
deps.notifications.error(
|
|
416
|
+
deps.notifications.errorMessage(error, "Subform \u0445\u0430\u0434\u0433\u0430\u043B\u0430\u0445\u0430\u0434 \u0430\u043B\u0434\u0430\u0430 \u0433\u0430\u0440\u043B\u0430\u0430")
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
return /* @__PURE__ */ jsx5(deps.DialogShell, { open, title, description: view.title, onClose, children: /* @__PURE__ */ jsx5(FormProvider3, { ...form, children: /* @__PURE__ */ jsxs4(
|
|
421
|
+
"form",
|
|
422
|
+
{
|
|
423
|
+
className: deps.dialogFormClassName,
|
|
424
|
+
onSubmit: (event) => {
|
|
425
|
+
event.stopPropagation();
|
|
426
|
+
void onSubmit(event);
|
|
427
|
+
},
|
|
428
|
+
noValidate: true,
|
|
429
|
+
children: [
|
|
430
|
+
view.form ? /* @__PURE__ */ jsx5(
|
|
431
|
+
deps.DialogFields,
|
|
432
|
+
{
|
|
433
|
+
formId: view.form.id,
|
|
434
|
+
viewId: view.id,
|
|
435
|
+
schema: view.form.schema,
|
|
436
|
+
namePrefix: "values"
|
|
437
|
+
}
|
|
438
|
+
) : null,
|
|
439
|
+
/* @__PURE__ */ jsx5(deps.DialogActions, { isSaving, onClose })
|
|
440
|
+
]
|
|
441
|
+
}
|
|
442
|
+
) }) });
|
|
443
|
+
}
|
|
444
|
+
function createRuntimeSubformFieldComponent(deps) {
|
|
445
|
+
return function RuntimeSubformField({
|
|
446
|
+
subform,
|
|
447
|
+
parentEntry,
|
|
448
|
+
draftRows = [],
|
|
449
|
+
onDraftRowsChange
|
|
450
|
+
}) {
|
|
451
|
+
const [isEntryDialogOpen, setEntryDialogOpen] = useState3(false);
|
|
452
|
+
const [editing, setEditing] = useState3(null);
|
|
453
|
+
const initializedDraft = useRef(false);
|
|
454
|
+
const linkedValue = parentValue(parentEntry, subform.parentField);
|
|
455
|
+
const isDraftMode = Boolean(onDraftRowsChange);
|
|
456
|
+
const canFetchBackendRows = Boolean(linkedValue);
|
|
457
|
+
const canUseBackendRows = canFetchBackendRows && !isDraftMode;
|
|
458
|
+
const isReady = Boolean(
|
|
459
|
+
subform.viewId && subform.childField && (linkedValue || onDraftRowsChange)
|
|
460
|
+
);
|
|
461
|
+
const tableState = deps.useDataTableState({ pageSize: 5 });
|
|
462
|
+
const query = useMemo5(
|
|
463
|
+
() => ({
|
|
464
|
+
...tableState.query,
|
|
465
|
+
filterField: subform.childField,
|
|
466
|
+
filterValue: linkedValue ?? ""
|
|
467
|
+
}),
|
|
468
|
+
[linkedValue, subform.childField, tableState.query]
|
|
469
|
+
);
|
|
470
|
+
const viewQuery = deps.hooks.useRuntimeBuilderView(subform.viewId, Boolean(subform.viewId));
|
|
471
|
+
const entriesQuery = deps.hooks.useBuilderViewEntries(
|
|
472
|
+
subform.viewId,
|
|
473
|
+
query,
|
|
474
|
+
isReady && (canUseBackendRows || isDraftMode && canFetchBackendRows)
|
|
475
|
+
);
|
|
476
|
+
const save = deps.hooks.useSaveBuilderViewEntry(subform.viewId);
|
|
477
|
+
const view = viewQuery.data;
|
|
478
|
+
const columns = useMemo5(() => view ? deps.buildColumns(view) : [], [view]);
|
|
479
|
+
const rows = (canUseBackendRows ? entriesQuery.data?.items ?? [] : draftRows).filter(
|
|
480
|
+
(row) => row.values._delete !== true
|
|
481
|
+
);
|
|
482
|
+
const table = deps.useDataTable({
|
|
483
|
+
columns,
|
|
484
|
+
data: rows,
|
|
485
|
+
rowCount: canUseBackendRows ? entriesQuery.data?.total ?? 0 : rows.length,
|
|
486
|
+
state: tableState,
|
|
487
|
+
getRowId: (row) => row.id
|
|
488
|
+
});
|
|
489
|
+
const canOpen = isReady && Boolean(view?.form) && !viewQuery.isPending;
|
|
490
|
+
const selectedIds = new Set(table.selectedIds);
|
|
491
|
+
useEffect2(() => {
|
|
492
|
+
if (!isDraftMode || !view || initializedDraft.current || !entriesQuery.data) return;
|
|
493
|
+
initializedDraft.current = true;
|
|
494
|
+
onDraftRowsChange?.({
|
|
495
|
+
subformKey: subform.key,
|
|
496
|
+
dataKey: viewTableName(view),
|
|
497
|
+
rows: entriesQuery.data.items
|
|
498
|
+
});
|
|
499
|
+
}, [entriesQuery.data, isDraftMode, onDraftRowsChange, subform.key, view]);
|
|
500
|
+
return /* @__PURE__ */ jsxs4(
|
|
501
|
+
deps.Shell,
|
|
502
|
+
{
|
|
503
|
+
label: subform.label,
|
|
504
|
+
buttonLabel: subformButtonLabel(subform),
|
|
505
|
+
canOpen,
|
|
506
|
+
missingParentDescription: !linkedValue && !onDraftRowsChange ? `\u042D\u0445\u043B\u044D\u044D\u0434 parent \u043C\u04E9\u0440\u0438\u0439\u0433 \u0445\u0430\u0434\u0433\u0430\u043B\u043D\u0430, \u044D\u0441\u0432\u044D\u043B ${subform.parentField} \u0442\u0430\u043B\u0431\u0430\u0440\u044B\u043D \u0443\u0442\u0433\u044B\u0433 \u0431\u04E9\u0433\u043B\u04E9\u043D\u04E9.` : void 0,
|
|
507
|
+
onAdd: () => {
|
|
508
|
+
setEditing(null);
|
|
509
|
+
setEntryDialogOpen(true);
|
|
510
|
+
},
|
|
511
|
+
children: [
|
|
512
|
+
view?.list && (linkedValue || draftRows.length > 0) ? /* @__PURE__ */ jsx5(
|
|
513
|
+
deps.DataPanel,
|
|
514
|
+
{
|
|
515
|
+
controller: table,
|
|
516
|
+
isLoading: viewQuery.isPending || canUseBackendRows && entriesQuery.isPending,
|
|
517
|
+
showDraftDelete: isDraftMode && table.selectedIds.length > 0,
|
|
518
|
+
onRowClick: (entry) => {
|
|
519
|
+
setEditing(entry);
|
|
520
|
+
setEntryDialogOpen(true);
|
|
521
|
+
},
|
|
522
|
+
onDeleteSelected: () => {
|
|
523
|
+
if (!view) return;
|
|
524
|
+
onDraftRowsChange?.({
|
|
525
|
+
subformKey: subform.key,
|
|
526
|
+
dataKey: viewTableName(view),
|
|
527
|
+
rows: draftRows.filter((row) => !(selectedIds.has(row.id) && row.values._draftNew === true)).map(
|
|
528
|
+
(row) => selectedIds.has(row.id) ? { ...row, values: { ...row.values, _delete: true } } : row
|
|
529
|
+
)
|
|
530
|
+
});
|
|
531
|
+
table.clearSelection();
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
) : null,
|
|
535
|
+
view ? /* @__PURE__ */ jsx5(
|
|
536
|
+
RuntimeSubformEntryDialog,
|
|
537
|
+
{
|
|
538
|
+
open: isEntryDialogOpen,
|
|
539
|
+
title: subform.label,
|
|
540
|
+
view,
|
|
541
|
+
childField: subform.childField,
|
|
542
|
+
parentValue: linkedValue,
|
|
543
|
+
editing,
|
|
544
|
+
isSaving: canUseBackendRows ? save.isPending : false,
|
|
545
|
+
deps,
|
|
546
|
+
onClose: () => {
|
|
547
|
+
setEntryDialogOpen(false);
|
|
548
|
+
setEditing(null);
|
|
549
|
+
},
|
|
550
|
+
onSave: async (args) => {
|
|
551
|
+
if (!isDraftMode && canUseBackendRows) {
|
|
552
|
+
await save.mutateAsync(args);
|
|
553
|
+
await entriesQuery.refetch();
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
if (!onDraftRowsChange) return;
|
|
557
|
+
const nextEntry = {
|
|
558
|
+
...draftEntry(args.input, editing),
|
|
559
|
+
viewId: view.id
|
|
560
|
+
};
|
|
561
|
+
onDraftRowsChange({
|
|
562
|
+
subformKey: subform.key,
|
|
563
|
+
dataKey: viewTableName(view),
|
|
564
|
+
rows: editing ? draftRows.map((row) => row.id === editing.id ? nextEntry : row) : [...draftRows, nextEntry]
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
) : null
|
|
569
|
+
]
|
|
570
|
+
}
|
|
571
|
+
);
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
export {
|
|
575
|
+
createDynamicFormComponent,
|
|
576
|
+
createDynamicListComponent,
|
|
577
|
+
createDynamicViewComponent,
|
|
578
|
+
createRuntimeEntryDialogComponent,
|
|
579
|
+
createRuntimeSubformFieldComponent
|
|
580
|
+
};
|
|
581
|
+
//# sourceMappingURL=index.js.map
|