@rebasepro/cms 0.22.0 → 0.24.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/{CollectionEditorDialog-CUtfS_49.js → CollectionEditorDialog-p5-I-Apu.js} +181 -97
- package/dist/CollectionEditorDialog-p5-I-Apu.js.map +1 -0
- package/dist/{PropertyEditView-BpEbC0Q2.js → PropertyEditView-DDB_X-00.js} +55 -23
- package/dist/PropertyEditView-DDB_X-00.js.map +1 -0
- package/dist/{RouterCollectionsStudioView-EK8Knq5I.js → RouterCollectionsStudioView-SGBV4CMg.js} +34 -10
- package/dist/RouterCollectionsStudioView-SGBV4CMg.js.map +1 -0
- package/dist/{SchemaChangeDialog-BeP0A4MJ.js → SchemaChangeDialog-CcFSXvkK.js} +33 -12
- package/dist/SchemaChangeDialog-CcFSXvkK.js.map +1 -0
- package/dist/collection_editor/_cms_internals.d.ts +3 -1
- package/dist/collection_editor/liveSchemaClient.d.ts +38 -18
- package/dist/collection_editor/relation_kinds.d.ts +11 -0
- package/dist/collection_editor/serializable_types.d.ts +2 -0
- package/dist/collection_editor/types/config_controller.d.ts +11 -0
- package/dist/collection_editor/ui/collection_editor/CollectionStudioView.d.ts +8 -1
- package/dist/collection_editor/useLiveSchemaEditing.d.ts +5 -5
- package/dist/collection_editor/useLocalCollectionsConfigController.d.ts +7 -0
- package/dist/collection_editor_ui.js +5 -5
- package/dist/components/ClearFilterSortButton.d.ts +7 -1
- package/dist/components/CollectionTableBinding/fields/CompactEntityCellField.d.ts +12 -13
- package/dist/components/CollectionTableBinding/fields/TableMultipleRelationField.d.ts +6 -3
- package/dist/components/CollectionTableBinding/fields/TableReferenceField.d.ts +8 -1
- package/dist/components/CollectionTableBinding/fields/TableRelationField.d.ts +8 -1
- package/dist/components/CollectionTableBinding/fields/TableRelationSelectorField.d.ts +13 -3
- package/dist/components/CollectionTableBinding/fields/VirtualTableSelect.d.ts +10 -0
- package/dist/components/CollectionTableBinding/fields/VirtualTableUserSelect.d.ts +10 -0
- package/dist/components/CollectionTableBinding/internal/EntityTableCell.d.ts +21 -0
- package/dist/components/CollectionTableBinding/internal/TableCellOpener.d.ts +45 -0
- package/dist/components/CollectionTableBinding/table_bindings.d.ts +24 -0
- package/dist/components/CollectionViewBinding/CollectionViewActions.d.ts +6 -1
- package/dist/components/CollectionViewBinding/CollectionViewBinding.d.ts +12 -3
- package/dist/components/CollectionViewBinding/FiltersDialog.d.ts +5 -0
- package/dist/components/EditViewBinding.d.ts +7 -1
- package/dist/components/EntityIdentityBar.d.ts +29 -4
- package/dist/components/EntityPreviewBinding.d.ts +8 -1
- package/dist/components/EntityPreviewNesting.d.ts +15 -0
- package/dist/components/NavigationLoadingErrorView.d.ts +16 -0
- package/dist/components/PickerTrigger.d.ts +17 -0
- package/dist/components/ReferenceTable/SelectionTableBinding.d.ts +12 -0
- package/dist/components/RelationSelector.d.ts +24 -7
- package/dist/components/UserSelector.d.ts +8 -1
- package/dist/components/common/ActionMenuLabel.d.ts +15 -0
- package/dist/components/common/default_entity_actions.d.ts +37 -0
- package/dist/data_export/export/additional_fields.d.ts +13 -0
- package/dist/data_export/export/export.d.ts +6 -0
- package/dist/data_import/components/ImportConversionProblems.d.ts +11 -0
- package/dist/data_import/components/index.d.ts +1 -0
- package/dist/data_import/utils/csv.d.ts +3 -2
- package/dist/data_import/utils/data.d.ts +81 -4
- package/dist/data_import/utils/id_column.d.ts +16 -0
- package/dist/data_import/utils/import_columns.d.ts +48 -0
- package/dist/editor/editor.d.ts +7 -1
- package/dist/editor/extensions/Image.d.ts +22 -1
- package/dist/editor/hooks/useProseMirror.d.ts +4 -1
- package/dist/editor.js +69 -7
- package/dist/editor.js.map +1 -1
- package/dist/{export-LvIpE7sE.js → export-DCHuPD1a.js} +65 -211
- package/dist/export-DCHuPD1a.js.map +1 -0
- package/dist/form/form_utils.d.ts +37 -0
- package/dist/form/resolve_form_properties.d.ts +26 -0
- package/dist/form/validation.d.ts +13 -0
- package/dist/{history-DG6Ln7CV.js → history-CBd6SBRs.js} +3 -2
- package/dist/{history-DG6Ln7CV.js.map → history-CBd6SBRs.js.map} +1 -1
- package/dist/hooks/useEntityDisplay.d.ts +4 -5
- package/dist/hooks/useRecordActions.d.ts +28 -6
- package/dist/hooks/useReferencePaths.d.ts +21 -0
- package/dist/hooks/useStorageReferenceResolver.d.ts +23 -0
- package/dist/{import-Cxnmpyjl.js → import-DjQoRrIU.js} +35 -54
- package/dist/import-DjQoRrIU.js.map +1 -0
- package/dist/index.js +191 -117
- package/dist/index.js.map +1 -1
- package/dist/preview/components/StorageMarkdown.d.ts +10 -0
- package/dist/preview/util.d.ts +29 -0
- package/dist/util/caller_scopes.d.ts +15 -0
- package/dist/util/entity_actions.d.ts +37 -1
- package/dist/util/fit_inline_actions.d.ts +20 -0
- package/dist/util/navigation_utils.d.ts +14 -2
- package/dist/util/view_mode.d.ts +12 -18
- package/dist/{util-CyIcgnv2.js → util-BRvKFMLa.js} +5775 -3665
- package/dist/util-BRvKFMLa.js.map +1 -0
- package/dist/{util-DB31IoJE.js → util-D7ebttKT.js} +39 -2
- package/dist/util-D7ebttKT.js.map +1 -0
- package/package.json +9 -9
- package/dist/CollectionEditorDialog-CUtfS_49.js.map +0 -1
- package/dist/PropertyEditView-BpEbC0Q2.js.map +0 -1
- package/dist/RouterCollectionsStudioView-EK8Knq5I.js.map +0 -1
- package/dist/SchemaChangeDialog-BeP0A4MJ.js.map +0 -1
- package/dist/export-LvIpE7sE.js.map +0 -1
- package/dist/import-Cxnmpyjl.js.map +0 -1
- package/dist/util-CyIcgnv2.js.map +0 -1
- package/dist/util-DB31IoJE.js.map +0 -1
|
@@ -1,183 +1,10 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import {
|
|
2
|
+
import { $t as downloadDataAsCsv, On as resolveSelection, Qt as downloadBlob, Rn as useAdminContext, an as unescapeCsvFormula, en as downloadEntitiesExport, in as getEntityJsonExportableData, jn as walkEntityPages, kn as selectionQueryToFindParams, nn as escapeCsvFormula, rn as getEntityCSVExportableData, tn as entryToCSVRow } from "./util-BRvKFMLa.js";
|
|
3
3
|
import React, { useCallback, useMemo } from "react";
|
|
4
4
|
import { Alert, BooleanSwitchWithLabel, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, DownloadIcon, IconButton, Label, RadioGroup, RadioGroupItem, Tooltip, iconSize } from "@rebasepro/ui";
|
|
5
5
|
import { getDefaultValuesFor } from "@rebasepro/common";
|
|
6
6
|
import { useData, useTranslation } from "@rebasepro/app";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
-
import { getArrayValuesCount, getValueInPath } from "@rebasepro/utils";
|
|
9
|
-
//#region src/data_export/export/export.ts
|
|
10
|
-
function downloadEntitiesExport({ data, additionalData, properties, propertiesOrder, name, flattenArrays, additionalHeaders, exportType, dateExportType }) {
|
|
11
|
-
console.debug("Downloading export", {
|
|
12
|
-
dataLength: data.length,
|
|
13
|
-
properties,
|
|
14
|
-
exportType,
|
|
15
|
-
dateExportType
|
|
16
|
-
});
|
|
17
|
-
if (exportType === "csv") {
|
|
18
|
-
const headers = getExportHeaders(properties, propertiesOrder, additionalHeaders, flattenArrays ? getArrayValuesCount(data.map((d) => d.values)) : {});
|
|
19
|
-
const exportableData = getEntityCSVExportableData(data, additionalData, properties, headers, dateExportType);
|
|
20
|
-
downloadBlob([entryToCSVRow(headers.map((h) => h.label)), ...exportableData.map((entry) => entryToCSVRow(entry))], `${name}.csv`, "text/csv");
|
|
21
|
-
} else {
|
|
22
|
-
const exportableData = getEntityJsonExportableData(data, additionalData, properties, dateExportType);
|
|
23
|
-
downloadBlob([JSON.stringify(exportableData, null, 2)], `${name}.json`, "application/json");
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function getEntityCSVExportableData(data, additionalData, properties, headers, dateExportType) {
|
|
27
|
-
const mergedData = data.map((e) => ({
|
|
28
|
-
id: e.id,
|
|
29
|
-
...processValuesForExport(e.values, properties, "csv", dateExportType)
|
|
30
|
-
}));
|
|
31
|
-
if (additionalData) additionalData.forEach((additional, index) => {
|
|
32
|
-
mergedData[index] = {
|
|
33
|
-
...mergedData[index],
|
|
34
|
-
...additional
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
return mergedData && mergedData.map((entry) => {
|
|
38
|
-
return headers.map((header) => getValueInPath(entry, header.key));
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
function getEntityJsonExportableData(data, additionalData, properties, dateExportType) {
|
|
42
|
-
const mergedData = data.map((e) => ({
|
|
43
|
-
id: e.id,
|
|
44
|
-
...processValuesForExport(e.values, properties, "json", dateExportType)
|
|
45
|
-
}));
|
|
46
|
-
if (additionalData) additionalData.forEach((additional, index) => {
|
|
47
|
-
mergedData[index] = {
|
|
48
|
-
...mergedData[index],
|
|
49
|
-
...additional
|
|
50
|
-
};
|
|
51
|
-
});
|
|
52
|
-
return mergedData;
|
|
53
|
-
}
|
|
54
|
-
function getExportHeaders(properties, propertiesOrder, additionalHeaders, arrayValuesCount) {
|
|
55
|
-
const headers = [{
|
|
56
|
-
label: "id",
|
|
57
|
-
key: "id"
|
|
58
|
-
}, ...(propertiesOrder ?? Object.keys(properties)).flatMap((childKey) => {
|
|
59
|
-
const property = properties[childKey];
|
|
60
|
-
if (!property) {
|
|
61
|
-
console.warn("Property not found", childKey, properties);
|
|
62
|
-
return [];
|
|
63
|
-
}
|
|
64
|
-
if (arrayValuesCount && arrayValuesCount[childKey] > 1) return Array.from({ length: arrayValuesCount[childKey] }, (_, i) => getHeaders(property, `${childKey}[${i}]`, "")).flat();
|
|
65
|
-
else return getHeaders(property, childKey, "");
|
|
66
|
-
})];
|
|
67
|
-
if (additionalHeaders) headers.push(...additionalHeaders.map((h) => ({
|
|
68
|
-
label: h,
|
|
69
|
-
key: h
|
|
70
|
-
})));
|
|
71
|
-
return headers;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Get headers for property. There could be more than one header per property
|
|
75
|
-
* @param property
|
|
76
|
-
* @param propertyKey
|
|
77
|
-
* @param prefix
|
|
78
|
-
*/
|
|
79
|
-
function getHeaders(property, propertyKey, prefix = "") {
|
|
80
|
-
const currentKey = prefix ? `${prefix}.${propertyKey}` : propertyKey;
|
|
81
|
-
if (property.type === "map" && property.properties) return Object.entries(property.properties).map(([childKey, p]) => getHeaders(p, childKey, currentKey)).flat();
|
|
82
|
-
else return [{
|
|
83
|
-
label: currentKey,
|
|
84
|
-
key: currentKey
|
|
85
|
-
}];
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Read whatever a `date` column actually arrived as into a `Date`.
|
|
89
|
-
*
|
|
90
|
-
* The driver/WebSocket path revives `{ __type: "date" }` into a `Date`; the REST
|
|
91
|
-
* path — which is the one the admin is on — leaves an ISO string, and an epoch
|
|
92
|
-
* number is what a re-imported export carries. Returns `undefined` for anything
|
|
93
|
-
* that is not a date, so the caller can pass the raw value through.
|
|
94
|
-
*/
|
|
95
|
-
function toExportableDate(inputValue) {
|
|
96
|
-
if (inputValue instanceof Date) return Number.isNaN(inputValue.getTime()) ? void 0 : inputValue;
|
|
97
|
-
if (typeof inputValue === "number" && Number.isFinite(inputValue)) return new Date(inputValue);
|
|
98
|
-
if (typeof inputValue === "string" && inputValue.trim() !== "") {
|
|
99
|
-
const parsed = new Date(inputValue);
|
|
100
|
-
return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function processValueForExport(inputValue, property, exportType, dateExportType) {
|
|
104
|
-
let value;
|
|
105
|
-
if (property.type === "map" && property.properties) value = processValuesForExport(inputValue, property.properties, exportType, dateExportType);
|
|
106
|
-
else if (property.type === "array") if (property.of && Array.isArray(inputValue)) if (Array.isArray(property.of)) value = property.of.map((p, i) => processValueForExport(inputValue[i], p, exportType, dateExportType));
|
|
107
|
-
else if (property.of.type === "map") value = exportType === "csv" ? inputValue.map((e) => JSON.stringify(e)) : inputValue.map((e) => processValueForExport(e, property.of, exportType, dateExportType));
|
|
108
|
-
else value = inputValue.map((e) => processValueForExport(e, property.of, exportType, dateExportType));
|
|
109
|
-
else value = inputValue;
|
|
110
|
-
else if (property.type === "reference" && inputValue && typeof inputValue === "object" && "isEntityReference" in inputValue && typeof inputValue.isEntityReference === "function" && inputValue.isEntityReference()) {
|
|
111
|
-
const ref = inputValue ? inputValue : void 0;
|
|
112
|
-
value = ref ? ref.fullPath : null;
|
|
113
|
-
} else if (property.type === "date") {
|
|
114
|
-
const date = toExportableDate(inputValue);
|
|
115
|
-
value = date ? dateExportType === "timestamp" ? date.getTime() : date.toISOString() : inputValue;
|
|
116
|
-
} else value = inputValue;
|
|
117
|
-
return value;
|
|
118
|
-
}
|
|
119
|
-
function processValuesForExport(inputValues, properties, exportType, dateExportType) {
|
|
120
|
-
const updatedValues = Object.entries(properties).map(([key, property]) => {
|
|
121
|
-
const updatedValue = processValueForExport(inputValues && inputValues[key], property, exportType, dateExportType);
|
|
122
|
-
if (updatedValue === void 0) return {};
|
|
123
|
-
return { [key]: updatedValue };
|
|
124
|
-
}).reduce((a, b) => ({
|
|
125
|
-
...a,
|
|
126
|
-
...b
|
|
127
|
-
}), {});
|
|
128
|
-
return {
|
|
129
|
-
...inputValues,
|
|
130
|
-
...updatedValues
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Characters that make a spreadsheet read a cell as a formula rather than text.
|
|
135
|
-
*
|
|
136
|
-
* Quoting is CSV *escaping* and is not enough: Excel, LibreOffice Calc and
|
|
137
|
-
* Sheets strip the CSV quoting at parse time and then evaluate a cell whose
|
|
138
|
-
* first character is one of these — `=HYPERLINK(…)` exfiltrates neighbouring
|
|
139
|
-
* cells on click, `=cmd|'…'!A0` is worse.
|
|
140
|
-
*/
|
|
141
|
-
var FORMULA_TRIGGERS = [
|
|
142
|
-
"=",
|
|
143
|
-
"+",
|
|
144
|
-
"-",
|
|
145
|
-
"@",
|
|
146
|
-
" ",
|
|
147
|
-
"\r"
|
|
148
|
-
];
|
|
149
|
-
/**
|
|
150
|
-
* Neutralise a cell that a spreadsheet would otherwise evaluate, by prefixing a
|
|
151
|
-
* single quote — the standard OWASP mitigation. The apostrophe is consumed by
|
|
152
|
-
* the spreadsheet as "treat the rest as text", so the value reads back intact.
|
|
153
|
-
*/
|
|
154
|
-
function escapeCsvFormula(value) {
|
|
155
|
-
const first = value.charAt(0);
|
|
156
|
-
if (!FORMULA_TRIGGERS.includes(first)) return value;
|
|
157
|
-
if ((first === "-" || first === "+") && value.trim() !== "" && Number.isFinite(Number(value))) return value;
|
|
158
|
-
return "'" + value;
|
|
159
|
-
}
|
|
160
|
-
function toCSVCell(v) {
|
|
161
|
-
if (v === null || v === void 0) return "";
|
|
162
|
-
return "\"" + escapeCsvFormula(Array.isArray(v) ? JSON.stringify(v) : String(v)).replaceAll("\"", "\"\"") + "\"";
|
|
163
|
-
}
|
|
164
|
-
function entryToCSVRow(entry) {
|
|
165
|
-
return entry.map(toCSVCell).join(",") + "\r\n";
|
|
166
|
-
}
|
|
167
|
-
function downloadBlob(content, filename, contentType) {
|
|
168
|
-
const blob = new Blob(content, { type: contentType });
|
|
169
|
-
const url = URL.createObjectURL(blob);
|
|
170
|
-
const pom = document.createElement("a");
|
|
171
|
-
pom.href = url;
|
|
172
|
-
pom.setAttribute("download", filename);
|
|
173
|
-
pom.click();
|
|
174
|
-
}
|
|
175
|
-
function downloadDataAsCsv(data, name) {
|
|
176
|
-
if (data.length === 0) return;
|
|
177
|
-
const headers = Object.keys(data[0]);
|
|
178
|
-
downloadBlob([[headers.join(","), ...data.map((row) => headers.map((header) => toCSVCell(row[header])).join(","))].join("\r\n")], `${name}.csv`, "text/csv");
|
|
179
|
-
}
|
|
180
|
-
//#endregion
|
|
181
8
|
//#region src/data_export/export/fetch_export_data.ts
|
|
182
9
|
/**
|
|
183
10
|
* Rows asked for per request.
|
|
@@ -239,20 +66,28 @@ function BasicExportAction({ data, properties, propertiesOrder }) {
|
|
|
239
66
|
additionalData: [],
|
|
240
67
|
properties,
|
|
241
68
|
propertiesOrder,
|
|
242
|
-
name: "export
|
|
69
|
+
name: "export",
|
|
243
70
|
flattenArrays,
|
|
244
71
|
additionalHeaders: [],
|
|
245
72
|
exportType,
|
|
246
73
|
dateExportType
|
|
247
74
|
});
|
|
248
75
|
handleClose();
|
|
249
|
-
}, [
|
|
76
|
+
}, [
|
|
77
|
+
data,
|
|
78
|
+
properties,
|
|
79
|
+
propertiesOrder,
|
|
80
|
+
flattenArrays,
|
|
81
|
+
exportType,
|
|
82
|
+
dateExportType,
|
|
83
|
+
handleClose
|
|
84
|
+
]);
|
|
250
85
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Tooltip, {
|
|
251
86
|
title: "Export",
|
|
252
87
|
asChild: true,
|
|
253
88
|
children: /* @__PURE__ */ jsx(IconButton, {
|
|
89
|
+
"aria-label": "Export",
|
|
254
90
|
size: "small",
|
|
255
|
-
color: "primary",
|
|
256
91
|
onClick: handleClickOpen,
|
|
257
92
|
children: /* @__PURE__ */ jsx(DownloadIcon, { size: iconSize.small })
|
|
258
93
|
})
|
|
@@ -354,8 +189,7 @@ function BasicExportAction({ data, properties, propertiesOrder }) {
|
|
|
354
189
|
})] });
|
|
355
190
|
}
|
|
356
191
|
//#endregion
|
|
357
|
-
//#region src/data_export/export/
|
|
358
|
-
var DOCS_LIMIT = 500;
|
|
192
|
+
//#region src/data_export/export/additional_fields.ts
|
|
359
193
|
/**
|
|
360
194
|
* Additional-field builders run per row and may do I/O, so a `Promise.all` over
|
|
361
195
|
* the whole export is one request per row all at once. That was bounded only by
|
|
@@ -368,6 +202,43 @@ async function mapInChunks(items, fn, chunkSize = ADDITIONAL_FIELDS_CONCURRENCY)
|
|
|
368
202
|
for (let i = 0; i < items.length; i += chunkSize) results.push(...await Promise.all(items.slice(i, i + chunkSize).map(fn)));
|
|
369
203
|
return results;
|
|
370
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* The values of the export's extra columns, one record per entity: the
|
|
207
|
+
* collection's `exportable.additionalFields` and its `additionalFields` that
|
|
208
|
+
* declare a `value`, both in the same record.
|
|
209
|
+
*/
|
|
210
|
+
async function resolveAdditionalExportValues({ entities, exportFields, additionalFields, context }) {
|
|
211
|
+
const resolvedExportColumnsValues = exportFields ? await mapInChunks(entities, async (entity) => {
|
|
212
|
+
return (await Promise.all(exportFields.map(async (column) => {
|
|
213
|
+
return { [column.key]: await column.builder({
|
|
214
|
+
entity,
|
|
215
|
+
context
|
|
216
|
+
}) };
|
|
217
|
+
}))).reduce((a, b) => ({
|
|
218
|
+
...a,
|
|
219
|
+
...b
|
|
220
|
+
}), {});
|
|
221
|
+
}) : [];
|
|
222
|
+
const resolvedColumnsValues = additionalFields ? await mapInChunks(entities, async (entity) => {
|
|
223
|
+
return (await Promise.all(additionalFields.map(async (field) => {
|
|
224
|
+
if (!field.value) return {};
|
|
225
|
+
return { [field.key]: await field.value({
|
|
226
|
+
entity,
|
|
227
|
+
context
|
|
228
|
+
}) };
|
|
229
|
+
}))).reduce((a, b) => ({
|
|
230
|
+
...a,
|
|
231
|
+
...b
|
|
232
|
+
}), {});
|
|
233
|
+
}) : [];
|
|
234
|
+
return entities.map((_, i) => ({
|
|
235
|
+
...resolvedExportColumnsValues[i],
|
|
236
|
+
...resolvedColumnsValues[i]
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/data_export/export/ExportCollectionAction.tsx
|
|
241
|
+
var DOCS_LIMIT = 500;
|
|
371
242
|
function ExportCollectionAction({ collection, path, collectionEntitiesCount, selectionController, tableController, onAnalyticsEvent, exportAllowed, notAllowedView }) {
|
|
372
243
|
const { t } = useTranslation();
|
|
373
244
|
const exportConfig = typeof collection.exportable === "object" ? collection.exportable : void 0;
|
|
@@ -407,34 +278,16 @@ function ExportCollectionAction({ collection, path, collectionEntitiesCount, sel
|
|
|
407
278
|
const handleClose = useCallback(() => {
|
|
408
279
|
setOpen(false);
|
|
409
280
|
}, [setOpen]);
|
|
410
|
-
const fetchAdditionalFields = useCallback(
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
...a,
|
|
421
|
-
...b
|
|
422
|
-
}), {});
|
|
423
|
-
}) : [];
|
|
424
|
-
const resolvedColumnsValues = additionalFields ? await mapInChunks(entities, async (entity) => {
|
|
425
|
-
return (await Promise.all(additionalFields.map(async (field) => {
|
|
426
|
-
if (!field.value) return {};
|
|
427
|
-
return { [field.key]: await field.value({
|
|
428
|
-
entity,
|
|
429
|
-
context
|
|
430
|
-
}) };
|
|
431
|
-
}))).reduce((a, b) => ({
|
|
432
|
-
...a,
|
|
433
|
-
...b
|
|
434
|
-
}), {});
|
|
435
|
-
}) : [];
|
|
436
|
-
return [...resolvedExportColumnsValues, ...resolvedColumnsValues];
|
|
437
|
-
}, [exportConfig?.additionalFields]);
|
|
281
|
+
const fetchAdditionalFields = useCallback((entities) => resolveAdditionalExportValues({
|
|
282
|
+
entities,
|
|
283
|
+
exportFields: exportConfig?.additionalFields,
|
|
284
|
+
additionalFields: collection.additionalFields,
|
|
285
|
+
context
|
|
286
|
+
}), [
|
|
287
|
+
exportConfig?.additionalFields,
|
|
288
|
+
collection.additionalFields,
|
|
289
|
+
context
|
|
290
|
+
]);
|
|
438
291
|
const doDownload = useCallback(async (collection, exportConfig) => {
|
|
439
292
|
onAnalyticsEvent?.("export_collection", { collection: collection.slug });
|
|
440
293
|
setDataLoading(true);
|
|
@@ -511,8 +364,8 @@ function ExportCollectionAction({ collection, path, collectionEntitiesCount, sel
|
|
|
511
364
|
title: "Export",
|
|
512
365
|
asChild: true,
|
|
513
366
|
children: /* @__PURE__ */ jsx(IconButton, {
|
|
367
|
+
"aria-label": "Export",
|
|
514
368
|
size: "small",
|
|
515
|
-
color: "primary",
|
|
516
369
|
onClick: handleClickOpen,
|
|
517
370
|
children: /* @__PURE__ */ jsx(DownloadIcon, { size: iconSize.small })
|
|
518
371
|
})
|
|
@@ -658,9 +511,10 @@ var export_exports = /* @__PURE__ */ __exportAll({
|
|
|
658
511
|
escapeCsvFormula: () => escapeCsvFormula,
|
|
659
512
|
fetchAllEntitiesForExport: () => fetchAllEntitiesForExport,
|
|
660
513
|
getEntityCSVExportableData: () => getEntityCSVExportableData,
|
|
661
|
-
getEntityJsonExportableData: () => getEntityJsonExportableData
|
|
514
|
+
getEntityJsonExportableData: () => getEntityJsonExportableData,
|
|
515
|
+
unescapeCsvFormula: () => unescapeCsvFormula
|
|
662
516
|
});
|
|
663
517
|
//#endregion
|
|
664
|
-
export { MAX_EXPORT_ROWS as a,
|
|
518
|
+
export { MAX_EXPORT_ROWS as a, EXPORT_PAGE_SIZE as i, ExportCollectionAction as n, fetchAllEntitiesForExport as o, BasicExportAction as r, export_exports as t };
|
|
665
519
|
|
|
666
|
-
//# sourceMappingURL=export-
|
|
520
|
+
//# sourceMappingURL=export-DCHuPD1a.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-DCHuPD1a.js","names":[],"sources":["../src/data_export/export/fetch_export_data.ts","../src/data_export/export/BasicExportAction.tsx","../src/data_export/export/additional_fields.ts","../src/data_export/export/ExportCollectionAction.tsx","../src/data_export/export/index.ts"],"sourcesContent":["import { Entity, FindParams } from \"@rebasepro/types\";\nimport { EntityPageAccessor, walkEntityPages } from \"../../util/entity_pages\";\n\n/**\n * Rows asked for per request.\n *\n * Kept at or below the server's `MAX_LIST_LIMIT`, which is now the largest page\n * a read will serve *and refuses* anything larger — it used to clamp silently,\n * and asking for 100 000 while receiving 1 000 looked exactly like a collection\n * with 1 000 rows in it. The walk still never trusts the page size it asked for:\n * it advances by the rows it was actually handed.\n */\nexport const EXPORT_PAGE_SIZE = 500;\n\n/**\n * Most rows one browser-side export will materialise.\n *\n * Everything here is built in memory and handed to a Blob, so there is a\n * ceiling whether or not it is named. Naming it means going over it **throws**\n * instead of writing a short file that reads like the whole collection.\n */\nexport const MAX_EXPORT_ROWS = 100_000;\n\nexport interface FetchAllForExportParams<M extends Record<string, unknown>> {\n /** The collection accessor to read through — `dataClient.collection(path)`. */\n accessor: EntityPageAccessor<M>;\n /** Query to export. The window (`limit` / `offset` / `page`) is owned by the walk. */\n params?: Omit<FindParams<M>, \"limit\" | \"offset\" | \"page\">;\n pageSize?: number;\n maxRows?: number;\n /** Called after every page with the rows so far, and the total when the server reports one. */\n onProgress?: (loaded: number, total: number | undefined) => void;\n}\n\n/**\n * Read **every** row matching a query, for an export file.\n *\n * The export used to hand back the 50 rows an unwindowed `find({})` resolves to\n * as `<collection>.csv`, with nothing saying it was a sample, next to a dialog\n * warning that the collection had 100 000 documents. The walk is in\n * {@link walkEntityPages}; what belongs to export is the ceiling — an export is\n * built in memory and handed to a Blob — and what to say on hitting it.\n *\n * @throws When more than `maxRows` rows match.\n */\nexport async function fetchAllEntitiesForExport<M extends Record<string, unknown>>({\n accessor,\n params,\n pageSize = EXPORT_PAGE_SIZE,\n maxRows = MAX_EXPORT_ROWS,\n onProgress\n}: FetchAllForExportParams<M>): Promise<Entity<M>[]> {\n return walkEntityPages<M>({\n accessor,\n params,\n pageSize,\n maxRows,\n onProgress,\n onCeiling: (max) =>\n `This export would contain more than ${max.toLocaleString()} rows, which is more than the ` +\n \"browser can build in one file. Filter the collection down, or export it from the server.\"\n });\n}\n","\nimport React, { useCallback } from \"react\";\n\nimport { useTranslation } from \"@rebasepro/app\";\nimport { Entity, Properties } from \"@rebasepro/types\";\nimport {\n BooleanSwitchWithLabel,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n DownloadIcon,\n IconButton,\n iconSize,\n Label,\n RadioGroup,\n RadioGroupItem,\n Tooltip,\n Typography\n} from \"@rebasepro/ui\";\nimport { downloadEntitiesExport } from \"./export\";\n\nexport type BasicExportActionProps = {\n data: Entity<any>[];\n properties: Properties;\n propertiesOrder?: string[];\n}\n\nexport function BasicExportAction({\n data,\n properties,\n propertiesOrder\n}: BasicExportActionProps) {\n\n const { t } = useTranslation();\n const dateRef = React.useRef<Date>(new Date());\n const [flattenArrays, setFlattenArrays] = React.useState<boolean>(true);\n const [exportType, setExportType] = React.useState<\"csv\" | \"json\">(\"csv\");\n const [dateExportType, setDateExportType] = React.useState<\"timestamp\" | \"string\">(\"string\");\n\n const [open, setOpen] = React.useState(false);\n\n const handleClickOpen = useCallback(() => {\n setOpen(true);\n }, [setOpen]);\n\n const handleClose = useCallback(() => {\n setOpen(false);\n }, [setOpen]);\n\n const onOkClicked = useCallback(() => {\n downloadEntitiesExport({\n data,\n additionalData: [],\n properties,\n propertiesOrder,\n // The writer appends the extension for the chosen format.\n name: \"export\",\n flattenArrays,\n additionalHeaders: [],\n exportType,\n dateExportType\n });\n handleClose();\n }, [data, properties, propertiesOrder, flattenArrays, exportType, dateExportType, handleClose]);\n\n return <>\n\n <Tooltip title={\"Export\"}\n asChild={true}>\n <IconButton aria-label={\"Export\"}\n size={\"small\"}\n onClick={handleClickOpen}>\n <DownloadIcon\n size={iconSize.small}/>\n </IconButton>\n </Tooltip>\n\n <Dialog\n open={open}\n onOpenChange={setOpen}\n maxWidth={\"xl\"}>\n\n <DialogTitle variant={\"h6\"}>{t(\"export_data\")}</DialogTitle>\n\n <DialogContent className={\"flex flex-col gap-4 my-4\"}>\n\n <div>{t(\"download_table_csv\")}</div>\n\n <div className={\"flex flex-row gap-4\"}>\n <div className={\"p-4 flex flex-col\"}>\n <RadioGroup value={exportType} onValueChange={(v) => setExportType(v as \"csv\" | \"json\")}>\n <div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"csv\" id=\"radio-csv\"/>\n <Label htmlFor=\"radio-csv\">{t(\"csv\")}</Label>\n </div>\n <div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"json\" id=\"radio-json\"/>\n <Label htmlFor=\"radio-json\">{t(\"json\")}</Label>\n </div>\n </RadioGroup>\n </div>\n\n <div className={\"p-4 flex flex-col\"}>\n <RadioGroup value={dateExportType} onValueChange={(v) => setDateExportType(v as \"timestamp\" | \"string\")}>\n <div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"timestamp\" id=\"radio-timestamp\"/>\n <Label htmlFor=\"radio-timestamp\">{t(\"dates_as_timestamps\")} ({dateRef.current.getTime()})</Label>\n </div>\n <div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"string\" id=\"radio-string\"/>\n <Label htmlFor=\"radio-string\">{t(\"dates_as_strings\")} ({dateRef.current.toISOString()})</Label>\n </div>\n </RadioGroup>\n </div>\n </div>\n\n <BooleanSwitchWithLabel\n size={\"small\"}\n disabled={exportType !== \"csv\"}\n value={flattenArrays}\n onValueChange={setFlattenArrays}\n label={t(\"flatten_arrays\")}/>\n\n </DialogContent>\n\n <DialogActions>\n\n <Button onClick={handleClose}\n variant={\"text\"}>\n {t(\"cancel\")}\n </Button>\n\n <Button onClick={onOkClicked}>\n {t(\"download\")}\n </Button>\n\n </DialogActions>\n\n </Dialog>\n\n </>;\n}\n","import { Entity } from \"@rebasepro/types\";\nimport { AdditionalFieldDelegate, ExportMappingFunction, RebaseContext } from \"@rebasepro/cms-types\";\n\n/**\n * Additional-field builders run per row and may do I/O, so a `Promise.all` over\n * the whole export is one request per row all at once. That was bounded only by\n * the 50-row cap the export used to have; now that the read is paginated, it is\n * bounded here instead.\n */\nconst ADDITIONAL_FIELDS_CONCURRENCY = 50;\n\nasync function mapInChunks<T, R>(items: T[], fn: (item: T) => Promise<R>, chunkSize = ADDITIONAL_FIELDS_CONCURRENCY): Promise<R[]> {\n const results: R[] = [];\n for (let i = 0; i < items.length; i += chunkSize) {\n results.push(...await Promise.all(items.slice(i, i + chunkSize).map(fn)));\n }\n return results;\n}\n\n/**\n * The values of the export's extra columns, one record per entity: the\n * collection's `exportable.additionalFields` and its `additionalFields` that\n * declare a `value`, both in the same record.\n */\nexport async function resolveAdditionalExportValues<M extends Record<string, unknown>>({\n entities,\n exportFields,\n additionalFields,\n context\n}: {\n entities: Entity<M>[];\n exportFields: ExportMappingFunction[] | undefined;\n additionalFields: AdditionalFieldDelegate<M>[] | undefined;\n context: RebaseContext;\n}): Promise<Record<string, unknown>[]> {\n\n const resolvedExportColumnsValues: Record<string, unknown>[] = exportFields\n ? await mapInChunks(entities, async (entity) => {\n return (await Promise.all(exportFields.map(async (column) => {\n return {\n [column.key]: await column.builder({\n entity,\n context\n })\n };\n }))).reduce((a, b) => ({ ...a,\n...b }), {});\n })\n : [];\n\n const resolvedColumnsValues: Record<string, unknown>[] = additionalFields\n ? await mapInChunks(entities, async (entity) => {\n return (await Promise.all(additionalFields\n .map(async (field) => {\n if (!field.value)\n return {};\n return {\n [field.key]: await field.value({\n entity,\n context\n })\n };\n }))).reduce((a, b) => ({ ...a,\n...b }), {});\n })\n : [];\n // One record per entity, index for index — the export merges\n // `additionalData[i]` into row `i`.\n return entities.map((_, i) => ({\n ...resolvedExportColumnsValues[i],\n ...resolvedColumnsValues[i]\n }));\n}\n","\nimport React, { useCallback, useMemo } from \"react\";\n\nimport {\n useAuthController,\n useCustomizationController,\n useData,\n useTranslation\n} from \"@rebasepro/app\";\nimport { useAdminContext } from \"../../hooks\";\nimport { Entity, User } from \"@rebasepro/types\";\nimport { CollectionActionsProps, ExportConfig, RebaseContext, AdminCollection, SelectionQuery } from \"@rebasepro/cms-types\";\nimport { getDefaultValuesFor } from \"@rebasepro/common\";\nimport {\n Alert,\n BooleanSwitchWithLabel,\n Button,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n DownloadIcon,\n IconButton,\n iconSize,\n Label,\n RadioGroup,\n RadioGroupItem,\n Tooltip\n} from \"@rebasepro/ui\";\nimport { downloadEntitiesExport } from \"./export\";\nimport { fetchAllEntitiesForExport, MAX_EXPORT_ROWS } from \"./fetch_export_data\";\nimport { resolveAdditionalExportValues } from \"./additional_fields\";\nimport { resolveSelection, selectionQueryToFindParams } from \"../../selection\";\n\nconst DOCS_LIMIT = 500;\n\nexport function ExportCollectionAction<M extends Record<string, unknown>, USER extends User>({\n collection,\n path,\n collectionEntitiesCount,\n selectionController,\n tableController,\n onAnalyticsEvent,\n exportAllowed,\n notAllowedView\n}: CollectionActionsProps<M, USER, AdminCollection<M>> & {\n exportAllowed?: (props: { collectionEntitiesCount: number, path: string, collection: AdminCollection }) => boolean;\n notAllowedView?: React.ReactNode;\n onAnalyticsEvent?: (event: string, params?: any) => void;\n}) {\n\n const { t } = useTranslation();\n\n const exportConfig = typeof collection.exportable === \"object\" ? collection.exportable : undefined;\n\n const dateRef = React.useRef<Date>(new Date());\n\n const [includeUndefinedValues, setIncludeUndefinedValues] = React.useState<boolean>(false);\n const [flattenArrays, setFlattenArrays] = React.useState<boolean>(true);\n const [exportType, setExportType] = React.useState<\"csv\" | \"json\">(\"csv\");\n const [dateExportType, setDateExportType] = React.useState<\"timestamp\" | \"string\">(\"string\");\n\n const context = useAdminContext<USER>();\n const dataClient = useData();\n\n // A selection narrows the export to it; with none, the export is the view\n // — filter, search and sort included.\n const exportSelection = selectionController?.hasSelection ? selectionController.selection : undefined;\n\n const liveQuery: SelectionQuery<M> = useMemo(() => ({\n path,\n filterValues: tableController?.filterValues,\n searchString: tableController?.searchString,\n sortBy: tableController?.sortBy\n }), [path, tableController?.filterValues, tableController?.searchString, tableController?.sortBy]);\n\n // Said before the download starts, not discovered halfway through it: the\n // walk refuses rather than writing a short file, so the dialog has to name\n // the ceiling it is about to hit.\n const rowsToExport = exportSelection ? selectionController?.selectedCount : collectionEntitiesCount;\n const tooManyToExport = rowsToExport !== undefined && rowsToExport > MAX_EXPORT_ROWS;\n\n const canExport = !exportAllowed || exportAllowed({\n collectionEntitiesCount: collectionEntitiesCount ?? 0,\n path,\n collection\n });\n\n const [dataLoading, setDataLoading] = React.useState<boolean>(false);\n const [dataLoadingError, setDataLoadingError] = React.useState<Error | undefined>();\n const [progress, setProgress] = React.useState<{ loaded: number, total?: number }>({ loaded: 0 });\n\n const [open, setOpen] = React.useState(false);\n\n const handleClickOpen = useCallback(() => {\n setOpen(true);\n }, [setOpen]);\n\n const handleClose = useCallback(() => {\n setOpen(false);\n }, [setOpen]);\n\n const fetchAdditionalFields = useCallback((entities: Entity<M>[]) => resolveAdditionalExportValues({\n entities,\n exportFields: exportConfig?.additionalFields,\n additionalFields: collection.additionalFields,\n context: context as RebaseContext\n }), [exportConfig?.additionalFields, collection.additionalFields, context]);\n\n const doDownload = useCallback(async (collection: AdminCollection<M>,\n exportConfig: ExportConfig<any> | undefined): Promise<boolean> => {\n\n onAnalyticsEvent?.(\"export_collection\", {\n collection: collection.slug\n });\n setDataLoading(true);\n setDataLoadingError(undefined);\n setProgress({ loaded: 0 });\n try {\n const onProgress = (loaded: number, total: number | undefined) => setProgress({ loaded, total });\n const accessor = dataClient.collection(path) as { find: (params?: any) => Promise<any> };\n\n // What the user is looking at, or what they picked out of it.\n //\n // Neither used to be true: the export read the collection with no\n // params at all, so a filtered, searched table of 40 rows exported\n // every row in the table — and ticking rows first changed nothing.\n const data = exportSelection\n ? await resolveSelection<M>({\n selection: exportSelection,\n accessor,\n maxRows: MAX_EXPORT_ROWS,\n onProgress\n })\n // Paginated, not `find({})`: an absent limit resolves to 50 rows\n // server-side, so the export used to be the first page of the\n // collection under a filename that read like all of it.\n : await fetchAllEntitiesForExport<M>({\n accessor,\n params: selectionQueryToFindParams(liveQuery),\n onProgress\n });\n const additionalData = await fetchAdditionalFields(data);\n const additionalHeaders = [\n ...exportConfig?.additionalFields?.map(column => column.key) ?? [],\n ...collection.additionalFields?.map(field => field.key) ?? []\n ];\n\n const defaultValues = includeUndefinedValues ? getDefaultValuesFor(collection.properties) : undefined;\n const dataWithDefaults = defaultValues\n ? data.map(entity => ({\n ...entity,\n values: { ...defaultValues,\n...entity.values }\n }))\n : data;\n downloadEntitiesExport({\n data: dataWithDefaults,\n additionalData,\n properties: collection.properties,\n propertiesOrder: collection.propertiesOrder,\n name: collection.name,\n flattenArrays,\n additionalHeaders,\n exportType,\n dateExportType\n });\n onAnalyticsEvent?.(\"export_collection_success\", {\n collection: collection.slug\n });\n return true;\n } catch (e) {\n console.error(\"Error loading export data\", e);\n setDataLoadingError(e as Error);\n return false;\n } finally {\n setDataLoading(false);\n }\n\n }, [onAnalyticsEvent, dataClient, path, exportSelection, liveQuery, fetchAdditionalFields, includeUndefinedValues, flattenArrays, exportType, dateExportType]);\n\n const onOkClicked = useCallback(() => {\n // The dialog stays open until the walk finishes: it is the only place a\n // multi-page export reports progress, or reports that it failed.\n doDownload(collection, exportConfig).then((downloaded) => {\n if (downloaded) handleClose();\n });\n }, [doDownload, collection, exportConfig, handleClose]);\n\n return <>\n\n <Tooltip title={\"Export\"}\n asChild={true}>\n <IconButton aria-label={\"Export\"}\n size={\"small\"}\n onClick={handleClickOpen}>\n <DownloadIcon size={iconSize.small}/>\n </IconButton>\n </Tooltip>\n\n <Dialog\n open={open}\n onOpenChange={setOpen}\n maxWidth={\"xl\"}>\n\n <DialogTitle variant={\"h6\"}>{t(\"export_data\")}</DialogTitle>\n\n <DialogContent className={\"flex flex-col gap-4 my-4\"}>\n\n {/* What is about to be downloaded, in as many words. The export\n now follows the selection, or the view's filter and search —\n so \"the content of this table\" would be describing something\n it no longer does. */}\n <div>{exportSelection\n ? (rowsToExport === undefined\n ? (t(\"export_selection_all_matching\", { collection: collection.name })\n ?? `Download every ${collection.name} matching the current filter as a ${exportType.toUpperCase()}`)\n : (t(\"export_selection_count\", {\n total: rowsToExport.toLocaleString(),\n format: exportType.toUpperCase()\n }) ?? `Download the ${rowsToExport.toLocaleString()} selected rows as a ${exportType.toUpperCase()}`))\n : t(\"download_table_csv\")}</div>\n\n {!exportSelection && collectionEntitiesCount !== undefined && collectionEntitiesCount > DOCS_LIMIT && !tooManyToExport &&\n <Alert color={\"warning\"}>\n <div>\n {t(\"large_number_of_documents\", { count: collectionEntitiesCount.toString() })}\n </div>\n </Alert>}\n\n {tooManyToExport &&\n <Alert color={\"error\"}>\n <div>\n {t(\"too_many_documents_to_export\", {\n count: (collectionEntitiesCount ?? 0).toString(),\n limit: MAX_EXPORT_ROWS.toString()\n })}\n </div>\n </Alert>}\n\n <div className={\"flex flex-row gap-4\"}>\n <div className={\"p-4 flex flex-col\"}>\n <RadioGroup value={exportType} onValueChange={(v) => setExportType(v as \"csv\" | \"json\")}>\n <div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"csv\" id=\"radio-csv\"/>\n <Label htmlFor=\"radio-csv\">{t(\"csv\")}</Label>\n </div>\n <div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"json\" id=\"radio-json\"/>\n <Label htmlFor=\"radio-json\">{t(\"json\")}</Label>\n </div>\n </RadioGroup>\n </div>\n\n <div className={\"p-4 flex flex-col\"}>\n <RadioGroup value={dateExportType} onValueChange={(v) => setDateExportType(v as \"timestamp\" | \"string\")}>\n <div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"timestamp\" id=\"radio-timestamp\"/>\n <Label htmlFor=\"radio-timestamp\">{t(\"dates_as_timestamps\")} ({dateRef.current.getTime()})</Label>\n </div>\n <div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"string\" id=\"radio-string\"/>\n <Label htmlFor=\"radio-string\">{t(\"dates_as_strings\")} ({dateRef.current.toISOString()})</Label>\n </div>\n </RadioGroup>\n </div>\n </div>\n\n <BooleanSwitchWithLabel\n size={\"small\"}\n disabled={exportType !== \"csv\"}\n value={flattenArrays}\n onValueChange={setFlattenArrays}\n label={t(\"flatten_arrays\")}/>\n\n <BooleanSwitchWithLabel\n size={\"small\"}\n value={includeUndefinedValues}\n onValueChange={setIncludeUndefinedValues}\n label={t(\"include_undefined_values\")}/>\n\n {dataLoadingError && <Alert color={\"error\"}>\n <div>{dataLoadingError.message}</div>\n </Alert>}\n\n {!canExport && notAllowedView}\n\n </DialogContent>\n\n <DialogActions>\n\n {dataLoading && <CircularProgress size={\"smallest\"}/>}\n\n {dataLoading && <Label>\n {progress.total !== undefined\n ? `${progress.loaded} / ${progress.total}`\n : `${progress.loaded}`}\n </Label>}\n\n <Button onClick={handleClose}\n variant={\"text\"}>\n {t(\"cancel\")}\n </Button>\n\n <Button onClick={onOkClicked}\n disabled={dataLoading || !canExport || tooManyToExport}>\n {t(\"download\")}\n </Button>\n\n </DialogActions>\n\n </Dialog>\n\n </>;\n}\n","export * from \"./export\";\nexport * from \"./fetch_export_data\";\nexport * from \"./BasicExportAction\";\nexport * from \"./ExportCollectionAction\";\n"],"mappings":"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"}
|
|
@@ -3,8 +3,45 @@ import type { Entity, EntityStatus, EntityValues } from "@rebasepro/types";
|
|
|
3
3
|
import type { AuthController } from "@rebasepro/cms-types";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
export declare function extractTouchedValues(values: unknown, touched: Record<string, boolean>): Record<string, unknown>;
|
|
6
|
+
/**
|
|
7
|
+
* The edit to carry or back up: each top-level property the user touched,
|
|
8
|
+
* **whole** — the same unit a save writes.
|
|
9
|
+
*
|
|
10
|
+
* `extractTouchedValues` builds a tree of only the touched paths, which is a
|
|
11
|
+
* diff, and a diff cannot say "this item is gone": a list that shrank from
|
|
12
|
+
* three items to one, or a key-value map with a key deleted, came back whole
|
|
13
|
+
* when the diff was merged onto the stored record. Carrying the property whole
|
|
14
|
+
* and overlaying it (see {@link overlayEdit}) shows exactly what the user left.
|
|
15
|
+
*
|
|
16
|
+
* Which properties count is still decided by the touched tree with its ghost
|
|
17
|
+
* containers removed, so a field merely focused carries nothing.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getTouchedPropertyValues<M extends Record<string, unknown>>(values: Partial<M>, touched: Record<string, boolean>): Partial<M>;
|
|
20
|
+
/**
|
|
21
|
+
* What a form shows when an edit is laid over `base`: each property the edit
|
|
22
|
+
* carries replaces the base's, whole.
|
|
23
|
+
*
|
|
24
|
+
* Not `mergeDeep`, which merged arrays of maps index by index and kept the base
|
|
25
|
+
* items past the edit's length, and kept map keys the edit had dropped — so a
|
|
26
|
+
* deleted section or key came back when the edit changed layout or a draft was
|
|
27
|
+
* restored, in a form that then read as clean.
|
|
28
|
+
*/
|
|
29
|
+
export declare function overlayEdit<M extends Record<string, unknown>>(base: Partial<M>, edit: Partial<M>): Partial<M>;
|
|
6
30
|
export declare function removeEmptyContainers(obj: unknown): unknown;
|
|
7
31
|
export declare function getChanges<T extends object>(source: Partial<T>, comparison: Partial<T>): Partial<T>;
|
|
32
|
+
/**
|
|
33
|
+
* What an update of a stored record sends: every top-level property whose value
|
|
34
|
+
* differs from the stored one — whole.
|
|
35
|
+
*
|
|
36
|
+
* Whole, because that is the unit an update replaces. It sets each property it
|
|
37
|
+
* carries, so the value sent *is* the property's new value: a map sent as the
|
|
38
|
+
* one key that changed would erase every key left out, a key-value map with a
|
|
39
|
+
* key removed would arrive empty, and a geopoint with one coordinate moved is
|
|
40
|
+
* refused for lacking the other. {@link getChanges} walks into plain objects to
|
|
41
|
+
* find what changed inside them, which is right for comparing a draft with the
|
|
42
|
+
* form and wrong for a write.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getChangedProperties<M extends Record<string, unknown>>(values: Partial<M>, storedValues: Partial<M>): Partial<M>;
|
|
8
45
|
/**
|
|
9
46
|
* What the local-changes backup still has to offer, given what the form is
|
|
10
47
|
* already showing — the answer the "unsaved local changes" banner is asking for.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Properties, Property } from "@rebasepro/types";
|
|
2
|
+
import type { ResolvePropertyProps } from "@rebasepro/common";
|
|
3
|
+
/**
|
|
4
|
+
* A property as the record form shows it and judges it, given the values the
|
|
5
|
+
* form holds now: its `dynamicProps` applied, then its `conditions`.
|
|
6
|
+
*
|
|
7
|
+
* One function for both, so what a field looks like and what the save checks
|
|
8
|
+
* cannot drift apart. They had: the field was resolved through `dynamicProps`
|
|
9
|
+
* and nothing else, and the save was judged against the raw declaration — so a
|
|
10
|
+
* `dynamicProps` validation was never checked, and a `conditions` rule written
|
|
11
|
+
* by the collection editor (and taught by the docs) did nothing anywhere.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveFormProperty<M extends Record<string, unknown> = Record<string, unknown>>(props: ResolvePropertyProps<M>): Property | null;
|
|
14
|
+
/**
|
|
15
|
+
* The properties the form validates on submit: each resolved against `values`
|
|
16
|
+
* (see {@link resolveFormProperty}), inside maps too, and only those the user
|
|
17
|
+
* can change.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolvePropertiesForValidation<M extends Record<string, unknown>>({ properties, propertyKey, isNew, ...props }: Omit<ResolvePropertyProps<M>, "property"> & {
|
|
20
|
+
properties: Properties;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the record is being created. A field marked
|
|
23
|
+
* `admin.filledByServer` is not required then: the server fills it.
|
|
24
|
+
*/
|
|
25
|
+
isNew?: boolean;
|
|
26
|
+
}): Properties;
|
|
@@ -18,4 +18,17 @@ interface PropertyContext<P extends Property> {
|
|
|
18
18
|
export declare function getEntitySchema<M extends Record<string, unknown>>(entityId: string | number | undefined, properties: Properties, customFieldValidator?: CustomFieldValidator): z.ZodObject<Record<string, ZodTypeAny>>;
|
|
19
19
|
export declare function mapPropertyToZod(propertyContext: PropertyContext<Property>): ZodTypeAny;
|
|
20
20
|
export declare function getZodMapObjectSchema({ property, entityId, customFieldValidator, name }: PropertyContext<MapProperty>): ZodTypeAny;
|
|
21
|
+
/**
|
|
22
|
+
* The values as they are to be written: every `trim`, `lowercase` and
|
|
23
|
+
* `uppercase` a string property declares applied to its value, inside maps and
|
|
24
|
+
* arrays too.
|
|
25
|
+
*
|
|
26
|
+
* They are documented as changing the value that is written, and only the
|
|
27
|
+
* panel applies them — the server checks what it receives. Validation judges
|
|
28
|
+
* the transformed value, so without this the form passed "my-slug" and sent
|
|
29
|
+
* " My-Slug ", which a `matches` on the server then refused.
|
|
30
|
+
*
|
|
31
|
+
* Returns `values` itself when nothing changes.
|
|
32
|
+
*/
|
|
33
|
+
export declare function applyValueTransforms<M extends Record<string, unknown>>(values: M, properties: Properties): M;
|
|
21
34
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Qn as PropertyPreview, Zr as getPropertyInPath$1, tt as useHistory } from "./util-BRvKFMLa.js";
|
|
2
2
|
import * as React$1 from "react";
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { ArrowLeftIcon, Chip, ErrorBoundary, HistoryIcon, IconButton, Label, Tooltip, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
@@ -263,6 +263,7 @@ function EntityHistoryView({ entity, collection, formContext, refreshToken }) {
|
|
|
263
263
|
title: t("history_revert") ?? "Revert to this version",
|
|
264
264
|
className: "m-2 grow-0 self-start",
|
|
265
265
|
children: /* @__PURE__ */ jsx(IconButton, {
|
|
266
|
+
"aria-label": t("history_revert") ?? "Revert to this version",
|
|
266
267
|
onClick: () => {
|
|
267
268
|
if (dirty) snackbarController.open({
|
|
268
269
|
message: t("history_revert_dirty") ?? "Please save or discard your changes before reverting",
|
|
@@ -310,4 +311,4 @@ function EntityHistoryView({ entity, collection, formContext, refreshToken }) {
|
|
|
310
311
|
//#endregion
|
|
311
312
|
export { EntityHistoryView };
|
|
312
313
|
|
|
313
|
-
//# sourceMappingURL=history-
|
|
314
|
+
//# sourceMappingURL=history-CBd6SBRs.js.map
|