@firecms/data_import_export 3.0.0-canary.43 → 3.0.0-canary.45

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/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@firecms/data_import_export",
3
3
  "type": "module",
4
- "version": "3.0.0-canary.43",
4
+ "version": "3.0.0-canary.45",
5
5
  "access": "public",
6
6
  "main": "./dist/index.umd.js",
7
7
  "module": "./dist/index.es.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "source": "src/index.ts",
10
10
  "dependencies": {
11
- "@firecms/core": "^3.0.0-canary.43",
12
- "@firecms/formex": "^3.0.0-canary.43",
13
- "@firecms/schema_inference": "^3.0.0-canary.43",
14
- "@firecms/ui": "^3.0.0-canary.43",
11
+ "@firecms/core": "^3.0.0-canary.45",
12
+ "@firecms/formex": "^3.0.0-canary.45",
13
+ "@firecms/schema_inference": "^3.0.0-canary.45",
14
+ "@firecms/ui": "^3.0.0-canary.45",
15
15
  "xlsx": "^0.18.5"
16
16
  },
17
17
  "peerDependencies": {
@@ -101,5 +101,5 @@
101
101
  "publishConfig": {
102
102
  "access": "public"
103
103
  },
104
- "gitHead": "fedcb0d43c504245dd76b702e4ab4479fa8592df"
104
+ "gitHead": "7f22bfa3bb20479ad23becd9c10f78cac1a3bc32"
105
105
  }
@@ -0,0 +1,137 @@
1
+ import React, { useCallback } from "react";
2
+
3
+ import { Entity, ResolvedProperties } from "@firecms/core";
4
+ import {
5
+ BooleanSwitchWithLabel,
6
+ Button,
7
+ cn,
8
+ Dialog,
9
+ DialogActions,
10
+ DialogContent,
11
+ focusedMixin,
12
+ GetAppIcon,
13
+ IconButton,
14
+ Tooltip,
15
+ Typography,
16
+ } from "@firecms/ui";
17
+ import { downloadEntitiesExport } from "./export";
18
+
19
+ export type BasicExportActionProps = {
20
+ data: Entity<any>[];
21
+ properties: ResolvedProperties;
22
+ propertiesOrder?: string[];
23
+ }
24
+
25
+ export function BasicExportAction({
26
+ data,
27
+ properties,
28
+ propertiesOrder
29
+ }: BasicExportActionProps) {
30
+
31
+ const dateRef = React.useRef<Date>(new Date());
32
+ const [flattenArrays, setFlattenArrays] = React.useState<boolean>(true);
33
+ const [exportType, setExportType] = React.useState<"csv" | "json">("csv");
34
+ const [dateExportType, setDateExportType] = React.useState<"timestamp" | "string">("string");
35
+
36
+ const [open, setOpen] = React.useState(false);
37
+
38
+ const handleClickOpen = useCallback(() => {
39
+ setOpen(true);
40
+ }, [setOpen]);
41
+
42
+ const handleClose = useCallback(() => {
43
+ setOpen(false);
44
+ }, [setOpen]);
45
+
46
+ const onOkClicked = useCallback(() => {
47
+ downloadEntitiesExport(data, [], properties, propertiesOrder, "export.csv", flattenArrays, [], exportType, dateExportType);
48
+ handleClose();
49
+ }, []);
50
+
51
+ return <>
52
+
53
+ <Tooltip title={"Export"}>
54
+ <IconButton color={"primary"} onClick={handleClickOpen}>
55
+ <GetAppIcon/>
56
+ </IconButton>
57
+ </Tooltip>
58
+
59
+ <Dialog
60
+ open={open}
61
+ onOpenChange={setOpen}
62
+ maxWidth={"xl"}>
63
+ <DialogContent className={"flex flex-col gap-4 my-4"}>
64
+
65
+ <Typography variant={"h6"}>Export data</Typography>
66
+
67
+ <div>Download the the content of this table as a CSV</div>
68
+
69
+ <div className={"flex flex-row gap-4"}>
70
+ <div className={"p-4 flex flex-col"}>
71
+ <div className="flex items-center">
72
+ <input id="radio-csv" type="radio" value="csv" name="exportType"
73
+ checked={exportType === "csv"}
74
+ onChange={() => setExportType("csv")}
75
+ className={cn(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
76
+ <label htmlFor="radio-csv"
77
+ className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">CSV</label>
78
+ </div>
79
+ <div className="flex items-center">
80
+ <input id="radio-json" type="radio" value="json" name="exportType"
81
+ checked={exportType === "json"}
82
+ onChange={() => setExportType("json")}
83
+ className={cn(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
84
+ <label htmlFor="radio-json"
85
+ className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">JSON</label>
86
+ </div>
87
+ </div>
88
+
89
+ <div className={"p-4 flex flex-col"}>
90
+ <div className="flex items-center">
91
+ <input id="radio-timestamp" type="radio" value="timestamp" name="dateExportType"
92
+ checked={dateExportType === "timestamp"}
93
+ onChange={() => setDateExportType("timestamp")}
94
+ className={cn(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
95
+ <label htmlFor="radio-timestamp"
96
+ className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">Dates as
97
+ timestamps ({dateRef.current.getTime()})</label>
98
+ </div>
99
+ <div className="flex items-center">
100
+ <input id="radio-string" type="radio" value="string" name="dateExportType"
101
+ checked={dateExportType === "string"}
102
+ onChange={() => setDateExportType("string")}
103
+ className={cn(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
104
+ <label htmlFor="radio-string"
105
+ className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">Dates as
106
+ strings ({dateRef.current.toISOString()})</label>
107
+ </div>
108
+ </div>
109
+ </div>
110
+
111
+ <BooleanSwitchWithLabel
112
+ size={"small"}
113
+ disabled={exportType !== "csv"}
114
+ value={flattenArrays}
115
+ onValueChange={setFlattenArrays}
116
+ label={"Flatten arrays"}/>
117
+
118
+ </DialogContent>
119
+
120
+ <DialogActions>
121
+
122
+ <Button onClick={handleClose}
123
+ variant={"text"}>
124
+ Cancel
125
+ </Button>
126
+
127
+ <Button variant="filled"
128
+ onClick={onOkClicked}>
129
+ Download
130
+ </Button>
131
+
132
+ </DialogActions>
133
+
134
+ </Dialog>
135
+
136
+ </>;
137
+ }
@@ -28,7 +28,7 @@ import {
28
28
  Tooltip,
29
29
  Typography,
30
30
  } from "@firecms/ui";
31
- import { downloadExport } from "./export";
31
+ import { downloadEntitiesExport } from "./export";
32
32
 
33
33
  const DOCS_LIMIT = 500;
34
34
 
@@ -139,7 +139,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
139
139
  ...exportConfig?.additionalFields?.map(column => column.key) ?? [],
140
140
  ...collection.additionalFields?.map(field => field.key) ?? []
141
141
  ];
142
- downloadExport(data, additionalData, collection, flattenArrays, additionalHeaders, exportType, dateExportType);
142
+ downloadEntitiesExport(data, additionalData, collection.properties, collection.propertiesOrder, collection.name, flattenArrays, additionalHeaders, exportType, dateExportType);
143
143
  onAnalyticsEvent?.("export_collection_success", {
144
144
  collection: collection.path
145
145
  });
@@ -403,6 +403,7 @@ export function ImportDataPreview<M extends Record<string, any>>({
403
403
  dataLoading: false,
404
404
  noMoreToLoad: false
405
405
  }}
406
+ enablePopupIcon={false}
406
407
  endAdornment={<div className={"h-12"}/>}
407
408
  filterable={false}
408
409
  sortable={false}
@@ -4,7 +4,6 @@ import {
4
4
  EntityReference,
5
5
  getArrayValuesCount,
6
6
  getValueInPath,
7
- ResolvedEntityCollection,
8
7
  ResolvedProperties,
9
8
  ResolvedProperty
10
9
  } from "@firecms/core";
@@ -14,37 +13,43 @@ interface Header {
14
13
  label: string;
15
14
  }
16
15
 
17
- export function downloadExport<M extends Record<string, any>>(data: Entity<M>[],
18
- additionalData: Record<string, any>[] | undefined,
19
- collection: ResolvedEntityCollection<M>,
20
- flattenArrays: boolean,
21
- additionalHeaders: string[] | undefined,
22
- exportType: "csv" | "json",
23
- dateExportType: "timestamp" | "string"
16
+ export function downloadEntitiesExport<M extends Record<string, any>>(data: Entity<M>[],
17
+ additionalData: Record<string, any>[] | undefined,
18
+ properties: ResolvedProperties<M>,
19
+ propertiesOrder: string[] | undefined,
20
+ name: string,
21
+ flattenArrays: boolean,
22
+ additionalHeaders: string[] | undefined,
23
+ exportType: "csv" | "json",
24
+ dateExportType: "timestamp" | "string"
24
25
  ) {
25
26
 
26
- console.debug("Downloading export", { dataLength: data.length, collection, exportType, dateExportType });
27
- const properties = collection.properties;
27
+ console.debug("Downloading export", {
28
+ dataLength: data.length,
29
+ properties,
30
+ exportType,
31
+ dateExportType
32
+ });
28
33
 
29
34
  if (exportType === "csv") {
30
35
  const arrayValuesCount = flattenArrays ? getArrayValuesCount(data.map(d => d.values)) : {};
31
- const headers = getExportHeaders(properties, additionalHeaders, arrayValuesCount);
32
- const exportableData = getCSVExportableData(data, additionalData, properties, headers, dateExportType);
36
+ const headers = getExportHeaders(properties, propertiesOrder, additionalHeaders, arrayValuesCount);
37
+ const exportableData = getEntityCSVExportableData(data, additionalData, properties, headers, dateExportType);
33
38
  const headersData = entryToCSVRow(headers.map(h => h.label));
34
39
  const csvData = exportableData.map(entry => entryToCSVRow(entry));
35
- downloadBlob([headersData, ...csvData], `${collection.name}.csv`, "text/csv");
40
+ downloadBlob([headersData, ...csvData], `${name}.csv`, "text/csv");
36
41
  } else {
37
- const exportableData = getJsonExportableData(data, additionalData, properties, dateExportType);
42
+ const exportableData = getEntityJsonExportableData(data, additionalData, properties, dateExportType);
38
43
  const json = JSON.stringify(exportableData, null, 2);
39
- downloadBlob([json], `${collection.name}.json`, "application/json");
44
+ downloadBlob([json], `${name}.json`, "application/json");
40
45
  }
41
46
  }
42
47
 
43
- export function getCSVExportableData(data: Entity<any>[],
44
- additionalData: Record<string, any>[] | undefined,
45
- properties: ResolvedProperties,
46
- headers: Header[],
47
- dateExportType: "timestamp" | "string"
48
+ export function getEntityCSVExportableData(data: Entity<any>[],
49
+ additionalData: Record<string, any>[] | undefined,
50
+ properties: ResolvedProperties,
51
+ headers: Header[],
52
+ dateExportType: "timestamp" | "string"
48
53
  ) {
49
54
 
50
55
  const mergedData: any[] = data.map(e => ({
@@ -63,10 +68,10 @@ export function getCSVExportableData(data: Entity<any>[],
63
68
  });
64
69
  }
65
70
 
66
- export function getJsonExportableData(data: Entity<any>[],
67
- additionalData: Record<string, any>[] | undefined,
68
- properties: ResolvedProperties,
69
- dateExportType: "timestamp" | "string"
71
+ export function getEntityJsonExportableData(data: Entity<any>[],
72
+ additionalData: Record<string, any>[] | undefined,
73
+ properties: ResolvedProperties,
74
+ dateExportType: "timestamp" | "string"
70
75
  ) {
71
76
 
72
77
  const mergedData: any[] = data.map(e => ({
@@ -84,13 +89,18 @@ export function getJsonExportableData(data: Entity<any>[],
84
89
  }
85
90
 
86
91
  function getExportHeaders<M extends Record<string, any>>(properties: ResolvedProperties<M>,
92
+ propertiesOrder: string[] | undefined,
87
93
  additionalHeaders: string[] | undefined,
88
94
  arrayValuesCount?: ArrayValuesCount): Header[] {
89
95
 
90
96
  const headers: Header[] = [
91
- { label: "id", key: "id" },
92
- ...Object.entries(properties)
93
- .flatMap(([childKey, property]) => {
97
+ {
98
+ label: "id",
99
+ key: "id"
100
+ },
101
+ ...(propertiesOrder ?? Object.keys(properties))
102
+ .flatMap((childKey) => {
103
+ const property = properties[childKey];
94
104
  if (arrayValuesCount && arrayValuesCount[childKey] > 1) {
95
105
  return Array.from({ length: arrayValuesCount[childKey] },
96
106
  (_, i) => getHeaders(property as ResolvedProperty, `${childKey}[${i}]`, ""))
@@ -102,7 +112,10 @@ function getExportHeaders<M extends Record<string, any>>(properties: ResolvedPro
102
112
  ];
103
113
 
104
114
  if (additionalHeaders) {
105
- headers.push(...additionalHeaders.map(h => ({ label: h, key: h })));
115
+ headers.push(...additionalHeaders.map(h => ({
116
+ label: h,
117
+ key: h
118
+ })));
106
119
  }
107
120
 
108
121
  return headers;
@@ -121,7 +134,10 @@ function getHeaders(property: ResolvedProperty, propertyKey: string, prefix = ""
121
134
  .map(([childKey, p]) => getHeaders(p, childKey, currentKey))
122
135
  .flat();
123
136
  } else {
124
- return [{ label: currentKey, key: currentKey }];
137
+ return [{
138
+ label: currentKey,
139
+ key: currentKey
140
+ }];
125
141
  }
126
142
  }
127
143
 
@@ -0,0 +1,4 @@
1
+ export * from "./export";
2
+ export * from "./BasicExportAction";
3
+ export * from "./ExportCollectionAction";
4
+ export * from "./ImportCollectionAction";
package/src/index.ts CHANGED
@@ -3,3 +3,4 @@ export * from "./components";
3
3
  export * from "./types";
4
4
  export * from "./utils";
5
5
  export * from "./hooks";
6
+ export * from "./export_import";
@@ -19,7 +19,7 @@ export function useImportExportPlugin(props?: ImportExportPluginProps): FireCMSP
19
19
 
20
20
  export type ImportExportPluginProps = {
21
21
  exportAllowed?: (props: ExportAllowedParams) => boolean;
22
- notAllowedView: React.ReactNode;
22
+ notAllowedView?: React.ReactNode;
23
23
  onAnalyticsEvent?: (event: string, params?: any) => void;
24
24
  }
25
25
  export type ExportAllowedParams = { collectionEntitiesCount: number, path: string, collection: EntityCollection };