fuse-importer 0.21.0 → 0.23.2
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/README.md +259 -0
- package/lib/esm/Importer/ImporterContextProvider.d.ts +31 -4
- package/lib/esm/Importer/ImporterContextProvider.js +40 -33
- package/lib/esm/Importer/Matcher/index.js +6 -5
- package/lib/esm/Importer/PreviewModeBanner.d.ts +2 -0
- package/lib/esm/Importer/PreviewModeBanner.js +20 -0
- package/lib/esm/Importer/Review/index.js +14 -18
- package/lib/esm/Importer/SpreadsheetReviewContextProvider.js +1 -1
- package/lib/esm/Importer/Stepper.js +10 -3
- package/lib/esm/Importer/Uploader/DragAndDropImporter/ImporterDropzone.js +4 -1
- package/lib/esm/Importer/Uploader/HeadersPreview.js +19 -17
- package/lib/esm/Importer/common/Spreadsheet/SpreadsheetContextProvider.js +7 -5
- package/lib/esm/Importer/common/Spreadsheet/common/FieldHeader.d.ts +7 -0
- package/lib/esm/Importer/common/Spreadsheet/common/FieldHeader.js +31 -0
- package/lib/esm/Importer/common/Spreadsheet/common/fields/SpeadsheetAutocomplete.d.ts +11 -6
- package/lib/esm/Importer/common/Spreadsheet/common/fields/SpeadsheetAutocomplete.js +6 -7
- package/lib/esm/Importer/common/Spreadsheet/common/fields/SpreadsheetInput.d.ts +4 -4
- package/lib/esm/Importer/common/Spreadsheet/common/fields/SpreadsheetInput.js +4 -5
- package/lib/esm/Importer/common/Spreadsheet/common/fields/index.d.ts +6 -5
- package/lib/esm/Importer/common/Spreadsheet/common/fields/index.js +14 -6
- package/lib/esm/Importer/common/Spreadsheet/common/index.d.ts +3 -0
- package/lib/esm/Importer/common/Spreadsheet/common/index.js +53 -9
- package/lib/esm/Importer/common/Spreadsheet/index.js +28 -57
- package/lib/esm/Importer/common/Spreadsheet/inputs/Boolean.d.ts +3 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/Boolean.js +18 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/EmailInput.d.ts +3 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/EmailInput.js +14 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/EnumInput.d.ts +6 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/EnumInput.js +30 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/FloatInput.d.ts +3 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/FloatInput.js +16 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/IntegerInput.d.ts +3 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/IntegerInput.js +16 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/StringInput.d.ts +3 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/StringInput.js +14 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/index.d.ts +13 -0
- package/lib/esm/Importer/common/Spreadsheet/inputs/index.js +20 -0
- package/lib/esm/Importer/common/index.d.ts +2 -2
- package/lib/esm/Importer/common/index.js +2 -2
- package/lib/esm/Importer/index.d.ts +1 -0
- package/lib/esm/Importer/index.js +3 -4
- package/lib/esm/Importer/theme.d.ts +225 -0
- package/lib/esm/Importer/theme.js +18 -0
- package/lib/esm/common/buttons.d.ts +15 -5
- package/lib/esm/common/buttons.js +13 -11
- package/lib/esm/common/index.js +1 -1
- package/lib/esm/common/inputs/Autocomplete.d.ts +2 -2
- package/lib/esm/common/inputs/TextInput.js +8 -1
- package/lib/esm/common/logo.js +1 -1
- package/lib/esm/styled/themeType.d.ts +1 -1
- package/lib/esm/styled/utils.d.ts +3 -0
- package/lib/esm/styled/utils.js +4 -3
- package/lib/esm/types.d.ts +7 -2
- package/lib/esm/types.js +4 -1
- package/lib/main.js +1 -1
- package/package.json +2 -2
- package/lib/esm/Importer/useCustomImporter.d.ts +0 -4
- package/lib/esm/Importer/useCustomImporter.js +0 -81
package/README.md
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<div>
|
|
2
|
+
<center>
|
|
3
|
+
<a href="https://fuse.flatironsdevelopment.com/users/sign-up?utm_source=npmjs&utm_medium=Fuse+importer+logo&utm_campaign=NPM">
|
|
4
|
+
<img src="https://wp.flatironsdevelopment.com/wp-content/uploads/2022/07/fuse-logo.png">
|
|
5
|
+
|
|
6
|
+
</a>
|
|
7
|
+
</center>
|
|
8
|
+
<br>
|
|
9
|
+
<br>
|
|
10
|
+
<p>
|
|
11
|
+
Flatirons Fuse is a customizable and powerful CSV Importer that works with all front-end languages.
|
|
12
|
+
</p>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Introduction](#introduction)
|
|
19
|
+
- [Registration](#registration)
|
|
20
|
+
- [Create Your First Template](#create-your-first-template)
|
|
21
|
+
- [Getting started](#getting-started)
|
|
22
|
+
- [More Examples](#more-examples)
|
|
23
|
+
- [Usage](#usage)
|
|
24
|
+
- [FuseImporter Hooks](#fuseimporter-hooks)
|
|
25
|
+
- [`show`](#show)
|
|
26
|
+
- [`close`](#close)
|
|
27
|
+
- [`onClose`](#onclose)
|
|
28
|
+
- [`onValidateRecord`](#onvalidaterecord)
|
|
29
|
+
- [`formatRecord`](#formatrecord)
|
|
30
|
+
- [`onSubmit`](#onsubmit)
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
You can use Yarn, NPM, or a CDN to install the importer.
|
|
35
|
+
|
|
36
|
+
Install with npm:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install fuse-importer --save
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Install with yarn:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
yarn add fuse-importer
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Install with CDN:
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<script
|
|
52
|
+
type="text/javascript"
|
|
53
|
+
src="https://unpkg.com/fuse-importer@latest.js"
|
|
54
|
+
></script>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Introduction
|
|
58
|
+
|
|
59
|
+
Flatirons Fuse is our solution to the inevitable difficulties of getting customer data into your solution. We provide a white-labeled, customizable CSV and Spreadsheet import experience that is above all effective and flexible. Not only do we save you the time and effort of developing your own import solution, but we also ensure that setup and integration with your systems are a breeze.
|
|
60
|
+
|
|
61
|
+
To get started with our solution, you're going to need a free account and to create a template on our platform: [Flatirons Fuse](https://fuse.flatironsdevelopment.com/users/sign-up?utm_source=npmjs&utm_medium=Flatirons+Fuse+Get+Started&utm_campaign=NPM)
|
|
62
|
+
|
|
63
|
+
To use the importer you will need two keys:
|
|
64
|
+
|
|
65
|
+
- Your **Organization's API key**: You can find this at the top of the [Templates page](#create-your-first-template).
|
|
66
|
+
- Your **Template's ID**: You can find this in the table on the [Templates page](#create-your-first-template).
|
|
67
|
+
|
|
68
|
+
For more information see our [full documentation.](https://fuse-docs.flatironsdevelopment.com?utm_source=npmjs&utm_medium=Fuse+importer+Full+Documentation&utm_campaign=NPM)
|
|
69
|
+
|
|
70
|
+
Check it out in action:
|
|
71
|
+
|
|
72
|
+
<center>
|
|
73
|
+
<a href="https://fuse.flatironsdevelopment.com/users/sign-up?utm_source=npmjs&utm_medium=Fuse+importer+Gif&utm_campaign=NPM">
|
|
74
|
+
<img src="https://wp.flatironsdevelopment.com/wp-content/uploads/2022/07/Flatirons-Fuse-users-Columns-7-July-2022.gif">
|
|
75
|
+
</a>
|
|
76
|
+
</center>
|
|
77
|
+
|
|
78
|
+
## Registration
|
|
79
|
+
|
|
80
|
+
If you don't have an account yet, you can go over the [Registration](https://fuse-docs.flatironsdevelopment.com/getting-started/registration?utm_source=npmjs&utm_medium=Registration&utm_campaign=NPM).
|
|
81
|
+
|
|
82
|
+
## Create Your First Template
|
|
83
|
+
|
|
84
|
+
If you don’t have a Template yet, you can go over [Create Your First Template](https://fuse-docs.flatironsdevelopment.com/getting-started/create-your-first-template?utm_source=npmjs&utm_medium=Create+Your+First+Template&utm_campaign=NPM). Template represents the fields and validations that your own system accepts, also known as your data schema.
|
|
85
|
+
|
|
86
|
+
## Getting started
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<!DOCTYPE html>
|
|
90
|
+
<html>
|
|
91
|
+
<head>
|
|
92
|
+
<script
|
|
93
|
+
type="text/javascript"
|
|
94
|
+
src="https://unpkg.com/fuse-importer@latest"
|
|
95
|
+
></script>
|
|
96
|
+
<script type="text/javascript">
|
|
97
|
+
const organizationApiKey = "YOUR ORGANIZATIONS API KEY";
|
|
98
|
+
const templateId = "YOUR TEMPLATE ID";
|
|
99
|
+
const importer = new FuseImporter(organizationApiKey, templateId);
|
|
100
|
+
|
|
101
|
+
importer.onValidateRecord = async (record) => {
|
|
102
|
+
// return no frontend validation errors
|
|
103
|
+
return {};
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
importer.onSubmit = async (records) => {
|
|
107
|
+
// an empty hash will tell us the import was successful!
|
|
108
|
+
return {
|
|
109
|
+
message: "Your data was imported successfully",
|
|
110
|
+
errors: {},
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
window.showFuseImporter = () => {
|
|
115
|
+
if (
|
|
116
|
+
organizationApiKey.indexOf("YOUR") !== -1 ||
|
|
117
|
+
templateId.indexOf("YOUR") !== -1
|
|
118
|
+
) {
|
|
119
|
+
window.confirm(
|
|
120
|
+
"You need to configure your organization api key and template id."
|
|
121
|
+
);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
importer.show();
|
|
125
|
+
};
|
|
126
|
+
</script>
|
|
127
|
+
</head>
|
|
128
|
+
|
|
129
|
+
<body>
|
|
130
|
+
<button onclick="javascript:showFuseImporter()">Show Importer</button>
|
|
131
|
+
</body>
|
|
132
|
+
</html>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## More Examples
|
|
136
|
+
|
|
137
|
+
- [Quickstart Examples](https://fuse-docs.flatironsdevelopment.com/installation/quickstart-examples?utm_source=npmjs&utm_medium=More+Examples+Quickstart+Examples&utm_campaign=NPM)
|
|
138
|
+
- [React.js](https://fuse-docs.flatironsdevelopment.com/use-cases/react.js-csv-importer?utm_source=npmjs&utm_medium=More+Examples+React&utm_campaign=NPM)
|
|
139
|
+
- [Angular](https://fuse-docs.flatironsdevelopment.com/use-cases/angular-csv-importer?utm_source=npmjs&utm_medium=More+Examples+Angular&utm_campaign=NPM)
|
|
140
|
+
- [Vue.js](https://fuse-docs.flatironsdevelopment.com/use-cases/vue.js-csv-importer?utm_source=npmjs&utm_medium=More+Examples+Vue&utm_campaign=NPM)
|
|
141
|
+
|
|
142
|
+
## Usage
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import FuseImporter from "fuse-importer";
|
|
146
|
+
|
|
147
|
+
const organizationApiKey = "YOUR ORGANIZATIONS API KEY";
|
|
148
|
+
const templateId = "YOUR TEMPLATE ID";
|
|
149
|
+
const importer = new FuseImporter(organizationApiKey, templateId);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### FuseImporter Hooks
|
|
153
|
+
|
|
154
|
+
Hooks allow you to show the importer, run validations, and submit records to your backend.
|
|
155
|
+
|
|
156
|
+
#### `show`
|
|
157
|
+
|
|
158
|
+
The show function does exactly what it sounds like; it launches the importer. Ensure you have initialized the importer properly beforehand. Use this function like so:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
importer.show();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### `close`
|
|
165
|
+
|
|
166
|
+
The close function allows you to remove the importer from the view.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
importer.close();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### `onClose`
|
|
173
|
+
|
|
174
|
+
The onClose function allows you to be notified when the importer is removed from the view.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
importer.onClose = () => {
|
|
178
|
+
console.log("The importer was closed!");
|
|
179
|
+
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### `onValidateRecord`
|
|
183
|
+
|
|
184
|
+
The `onValidationRecord` hook is called for each row (record) that a customer uploads. It allows you to specify custom frontend validations and display error messages in the importer for the user to fix. When you create a Template, each column contains an `internal key`. These internal keys represent the
|
|
185
|
+
|
|
186
|
+
Return values:
|
|
187
|
+
|
|
188
|
+
- If the record has no errors, we expect you to return an empty object: `return {}`;
|
|
189
|
+
- If the record has validation errors, you can specify them like so:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
importer.onValidateRecord = async (record) => {
|
|
193
|
+
const errors = {};
|
|
194
|
+
|
|
195
|
+
// force emails to include gmail
|
|
196
|
+
if (!record.email?.includes("gmail")) {
|
|
197
|
+
errors["email"] = "Email must be from Gmail";
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return errors;
|
|
201
|
+
};
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### `formatRecord`
|
|
205
|
+
|
|
206
|
+
The `formatRecord` hook can be used to format data automatically on behalf of a user. For example, if you have a field called `first_name` and you know that your system always requires `first_name` to be capitalized, you can use this callback to uppercase all `first_name` records like so:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
importer.formatRecord = (record) => {
|
|
210
|
+
const newRecord = { ...record };
|
|
211
|
+
|
|
212
|
+
// capitalize first letter in first_name
|
|
213
|
+
if (typeof newRecord.first_name === "string") {
|
|
214
|
+
newRecord.first_name =
|
|
215
|
+
newRecord.first_name.charAt(0).toUpperCase() +
|
|
216
|
+
newRecord.first_name.slice(1);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return newRecord;
|
|
220
|
+
};
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### `onSubmit`
|
|
224
|
+
|
|
225
|
+
`onSubmit` will pass all records to you after the user has made any necessary corrections to frontend validations. This callback expects you to submit the records to your backend in whatever way you typically would, we do not submit the records for you.
|
|
226
|
+
|
|
227
|
+
This callback also allows you to pass back any backend errors for specific rows. Each record passed to this callback will have a unique identifier under the key `record._meta.id`. In this case of a backend validation or error occurring, you can tell the importer to show a specific error to the user for a row. Note that after submission, only records with errors will remain visible to the user.
|
|
228
|
+
|
|
229
|
+
Return values:
|
|
230
|
+
|
|
231
|
+
- If there were no backend errors, pass back an empty errors object and a message to show to the user like so:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
importer.onSubmit = async (records) => {
|
|
235
|
+
// submit to backend here
|
|
236
|
+
let message = "Data imported successfully";
|
|
237
|
+
return {
|
|
238
|
+
message,
|
|
239
|
+
errors: {},
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
- If you find an error with a record in your backend, you can tell the importer to show the error to the user like so:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
importer.onSubmit = async (records) => {
|
|
248
|
+
// submit to your backend here
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
errors: {
|
|
252
|
+
[records[0]._meta.id]: {
|
|
253
|
+
email: "Email is already taken. Emails must be unique.",
|
|
254
|
+
first_name: "First Name must be capitalized.",
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
```
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Fuse from "fuse.js";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { ImporterOptions, OnValidateBatch, OnValidateRecord, FormatRecord } from "../types";
|
|
4
|
+
import { ImporterStylePreferences } from "./theme";
|
|
4
5
|
import { FuseApi } from "./FuseApi";
|
|
5
6
|
export declare const fusejsOptions: Fuse.IFuseOptions<string>;
|
|
6
7
|
export declare type HeaderMatchings = {
|
|
@@ -20,8 +21,28 @@ export declare type TemplateHeader = {
|
|
|
20
21
|
column_type: string;
|
|
21
22
|
internal_key: string;
|
|
22
23
|
values: string[];
|
|
24
|
+
pattern: string;
|
|
23
25
|
};
|
|
24
|
-
declare type
|
|
26
|
+
export declare type Organization = {
|
|
27
|
+
id: number;
|
|
28
|
+
name: string;
|
|
29
|
+
trial_mode: boolean;
|
|
30
|
+
plan: {
|
|
31
|
+
title: string;
|
|
32
|
+
allow_style_customization: boolean;
|
|
33
|
+
has_records_limit: boolean;
|
|
34
|
+
allowed_records: number;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export declare type Column = {
|
|
38
|
+
column_type: string;
|
|
39
|
+
internal_key: string;
|
|
40
|
+
label: string;
|
|
41
|
+
pattern: string;
|
|
42
|
+
required: boolean;
|
|
43
|
+
values: string[];
|
|
44
|
+
};
|
|
45
|
+
export declare type State = {
|
|
25
46
|
resetImport: () => void;
|
|
26
47
|
currentStepIndex: number;
|
|
27
48
|
setCurrentStepIndex: React.Dispatch<number>;
|
|
@@ -46,9 +67,10 @@ declare type State = {
|
|
|
46
67
|
fuseApi: FuseApi;
|
|
47
68
|
formatRecord: FormatRecord;
|
|
48
69
|
importerOptions: ImporterOptions;
|
|
49
|
-
|
|
70
|
+
theme: any;
|
|
71
|
+
logoURL: string;
|
|
50
72
|
};
|
|
51
|
-
declare type Props = {
|
|
73
|
+
export declare type Props = {
|
|
52
74
|
templateSlug: string;
|
|
53
75
|
apiToken: string;
|
|
54
76
|
options: ImporterOptions;
|
|
@@ -58,6 +80,11 @@ declare type Props = {
|
|
|
58
80
|
onValidateRecord?: OnValidateRecord;
|
|
59
81
|
onValidateBatch?: OnValidateBatch;
|
|
60
82
|
};
|
|
83
|
+
export declare type TemplateInfo = {
|
|
84
|
+
columns: Column[];
|
|
85
|
+
importer_style_preferences: ImporterStylePreferences;
|
|
86
|
+
organization: Organization;
|
|
87
|
+
name: string;
|
|
88
|
+
};
|
|
61
89
|
export declare const ImporterContextProvider: ({ templateSlug, apiToken, children, options, onClose, formatRecord, onValidateBatch, onValidateRecord, }: Props) => JSX.Element;
|
|
62
90
|
export declare const useImporterContext: () => State;
|
|
63
|
-
export {};
|
|
@@ -47,9 +47,10 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
47
47
|
};
|
|
48
48
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
49
49
|
import Fuse from "fuse.js";
|
|
50
|
-
import { createContext, useContext, useEffect, useState } from "react";
|
|
50
|
+
import { createContext, useContext, useEffect, useMemo, useState, } from "react";
|
|
51
51
|
import { FieldTypes, } from "../types";
|
|
52
52
|
import { getColumnType } from "./common/utils";
|
|
53
|
+
import { theme, getCustomTheme, } from "./theme";
|
|
53
54
|
import { FuseApi } from "./FuseApi";
|
|
54
55
|
export var fusejsOptions = {
|
|
55
56
|
includeScore: true,
|
|
@@ -63,21 +64,24 @@ export var ImporterContextProvider = function (_a) {
|
|
|
63
64
|
var _d = useState(null), templateError = _d[0], setTemplateError = _d[1];
|
|
64
65
|
var _e = useState(null), templateHeaders = _e[0], setTemplateHeaders = _e[1];
|
|
65
66
|
var _f = useState(null), uploadedData = _f[0], setUploadedData = _f[1];
|
|
66
|
-
var _g = useState(
|
|
67
|
-
var _h = useState(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
var
|
|
72
|
-
var
|
|
73
|
-
|
|
74
|
-
var
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
67
|
+
var _g = useState(null), organization = _g[0], setOrganization = _g[1];
|
|
68
|
+
var _h = useState({
|
|
69
|
+
theme: theme,
|
|
70
|
+
logoURL: null,
|
|
71
|
+
}), style = _h[0], setStyle = _h[1];
|
|
72
|
+
var _j = useState(null), templateHeaderMatchings = _j[0], setTemplateHeaderMatchings = _j[1];
|
|
73
|
+
var _k = useState(null), enumFieldValueMatchings = _k[0], setEnumFieldValueMatchings = _k[1];
|
|
74
|
+
var _l = useState(null), uploadedDataEnumFieldValues = _l[0], setUploadedDataEnumFieldValues = _l[1];
|
|
75
|
+
var fuseApi = useMemo(function () { return new FuseApi(options.env, apiToken); }, [
|
|
76
|
+
options.env,
|
|
77
|
+
apiToken,
|
|
78
|
+
]);
|
|
79
|
+
var uploadedDataHeaders = useMemo(function () {
|
|
80
|
+
return uploadedData &&
|
|
81
|
+
uploadedData[0].map(function (ud) {
|
|
82
|
+
return typeof ud === "number" ? ud.toString() : ud;
|
|
83
|
+
});
|
|
84
|
+
}, [uploadedData]);
|
|
81
85
|
useEffect(function () {
|
|
82
86
|
if (!templateHeaderMatchings)
|
|
83
87
|
return;
|
|
@@ -102,25 +106,30 @@ export var ImporterContextProvider = function (_a) {
|
|
|
102
106
|
});
|
|
103
107
|
setUploadedDataEnumFieldValues(_uploadedDataEnumFieldValues);
|
|
104
108
|
}, [templateHeaderMatchings]);
|
|
109
|
+
useEffect(function () {
|
|
110
|
+
if (uploadedData)
|
|
111
|
+
setHeadersFromDataset();
|
|
112
|
+
}, [uploadedData]);
|
|
105
113
|
useEffect(function () {
|
|
106
114
|
(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
107
|
-
var
|
|
108
|
-
var
|
|
109
|
-
return __generator(this, function (
|
|
110
|
-
switch (
|
|
115
|
+
var _a, organization_1, columns, importer_style_preferences, error_1, errorMsg;
|
|
116
|
+
var _b, _c;
|
|
117
|
+
return __generator(this, function (_d) {
|
|
118
|
+
switch (_d.label) {
|
|
111
119
|
case 0:
|
|
112
|
-
|
|
120
|
+
_d.trys.push([0, 2, , 3]);
|
|
113
121
|
return [4 /*yield*/, fuseApi.get("/api/v1/importer/templates/".concat(templateSlug))];
|
|
114
122
|
case 1:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
123
|
+
_a = (_d.sent()).data, organization_1 = _a.organization, columns = _a.columns, importer_style_preferences = _a.importer_style_preferences;
|
|
124
|
+
setOrganization(organization_1);
|
|
125
|
+
setTemplateHeaders(columns);
|
|
126
|
+
if (organization_1.plan && organization_1.plan.allow_style_customization) {
|
|
127
|
+
setStyle(getCustomTheme(importer_style_preferences));
|
|
128
|
+
}
|
|
120
129
|
return [3 /*break*/, 3];
|
|
121
130
|
case 2:
|
|
122
|
-
error_1 =
|
|
123
|
-
errorMsg = ((
|
|
131
|
+
error_1 = _d.sent();
|
|
132
|
+
errorMsg = ((_c = (_b = error_1.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) ||
|
|
124
133
|
"There was an error preventing the data importer from loading. Check your API Token and try again.";
|
|
125
134
|
setTemplateError(errorMsg);
|
|
126
135
|
return [3 /*break*/, 3];
|
|
@@ -129,10 +138,6 @@ export var ImporterContextProvider = function (_a) {
|
|
|
129
138
|
});
|
|
130
139
|
}); })();
|
|
131
140
|
}, []);
|
|
132
|
-
useEffect(function () {
|
|
133
|
-
if (uploadedData)
|
|
134
|
-
setHeadersFromDataset();
|
|
135
|
-
}, [uploadedData]);
|
|
136
141
|
var resetImport = function () {
|
|
137
142
|
setUploadedData(null);
|
|
138
143
|
setTemplateHeaderMatchings(null);
|
|
@@ -147,6 +152,7 @@ export var ImporterContextProvider = function (_a) {
|
|
|
147
152
|
});
|
|
148
153
|
setTemplateHeaderMatchings(_templateHeaderMatchings);
|
|
149
154
|
};
|
|
155
|
+
var inTrialMode = organization === null || organization === void 0 ? void 0 : organization.trial_mode;
|
|
150
156
|
var value = {
|
|
151
157
|
setUploadedData: setUploadedData,
|
|
152
158
|
currentStepIndex: currentStepIndex,
|
|
@@ -172,7 +178,8 @@ export var ImporterContextProvider = function (_a) {
|
|
|
172
178
|
inTrialMode: inTrialMode,
|
|
173
179
|
onClose: onClose,
|
|
174
180
|
fuseApi: fuseApi,
|
|
175
|
-
|
|
181
|
+
theme: style.theme,
|
|
182
|
+
logoURL: style.logoURL,
|
|
176
183
|
};
|
|
177
184
|
return (_jsx(ImporterContext.Provider, __assign({ value: value }, { children: children })));
|
|
178
185
|
};
|
|
@@ -14,6 +14,7 @@ var __assign = (this && this.__assign) || function () {
|
|
|
14
14
|
return __assign.apply(this, arguments);
|
|
15
15
|
};
|
|
16
16
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
17
|
+
import { useMemo } from "react";
|
|
17
18
|
import styled, { css } from "styled-components";
|
|
18
19
|
import { FieldTypes } from "../..";
|
|
19
20
|
import { Div } from "../../styled/utils";
|
|
@@ -26,11 +27,11 @@ var Dot = styled(Div)(function (_a) {
|
|
|
26
27
|
});
|
|
27
28
|
var Matcher = function () {
|
|
28
29
|
var _a = useImporterContext(), matcherSubstep = _a.matcherSubstep, templateHeaders = _a.templateHeaders;
|
|
29
|
-
var enumFieldsCount =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return (_jsxs(_Fragment, { children: [_jsx(Div, __assign({ centered: true }, { children: _jsx(Div, __assign({ w: 30, mt: 40, mb: 16, alignCenter: true, spaceBetween: true }, { children: enumFieldsCount > 0 &&
|
|
30
|
+
var enumFieldsCount = useMemo(function () {
|
|
31
|
+
return templateHeaders.filter(function (th) { return th.column_type === FieldTypes.enum; }).length;
|
|
32
|
+
}, [templateHeaders]);
|
|
33
|
+
var dotsUI = (_jsxs(_Fragment, { children: [_jsx(Dot, { isSelected: matcherSubstep === 0 }), _jsx(Dot, { isSelected: matcherSubstep === 1 })] }));
|
|
34
|
+
return (_jsxs(_Fragment, { children: [_jsx(Div, __assign({ centered: true }, { children: _jsx(Div, __assign({ w: 30, mt: 40, mb: 16, alignCenter: true, spaceBetween: true }, { children: enumFieldsCount > 0 && dotsUI })) })), matcherSubstep === 0 && _jsx(HeaderMatcher, {}), matcherSubstep === 1 && _jsx(EnumFieldsMatchers, {})] }));
|
|
34
35
|
};
|
|
35
36
|
export { Matcher };
|
|
36
37
|
var templateObject_1, templateObject_2;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { theme } from "../styled/theme";
|
|
14
|
+
import { Div, Span } from "../styled/utils";
|
|
15
|
+
import { useImporterContext } from "./ImporterContextProvider";
|
|
16
|
+
export var PreviewModeBanner = function () {
|
|
17
|
+
var previewMode = useImporterContext().importerOptions.previewMode;
|
|
18
|
+
return previewMode ? (_jsxs(Div, __assign({ bg: theme.colors.blue100, h: 80, alignCenter: true }, { children: [_jsx(Div, __assign({ ml: 33, mr: 8 }, { children: infoIcon })), _jsx(Span, __assign({ mr: 16 }, { children: "You’re currently in Preview mode." }))] }))) : null;
|
|
19
|
+
};
|
|
20
|
+
var infoIcon = (_jsxs("svg", __assign({ width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, { children: [_jsx("circle", { cx: "14", cy: "2.5", r: "2.5", fill: "#0EBEBE" }), _jsx("path", { d: "M15.5075 8H6L5 10.5L10 11L6.57474 18.9354C6.57474 18.9354 6.40657 20.1663 6.57474 20.9247C6.76276 21.7725 6.97074 22.2838 7.56727 22.9139C8.6672 24.0757 9.95115 24.1037 11.5374 23.9085C14.2577 23.5737 17 19.5 17 19.5L15.5075 17.9408C15.5075 17.9408 13.5223 19.93 12.5298 19.93C11.5373 19.93 11.5374 18.9354 11.5374 18.9354L15.5075 8Z", fill: "#0EBEBE" })] })));
|
|
@@ -14,18 +14,11 @@ import { useMemo } from "react";
|
|
|
14
14
|
import { Div } from "../../styled/utils";
|
|
15
15
|
import { FieldTypes } from "../../types";
|
|
16
16
|
import { Spreadsheet } from "../common/Spreadsheet";
|
|
17
|
+
import { isFloat, isInteger, isTrue } from "../common/Spreadsheet/common/index";
|
|
17
18
|
import { getColumnType, getSpreadsheetRowWidth } from "../common/utils";
|
|
18
19
|
import { useImporterContext } from "../ImporterContextProvider";
|
|
19
20
|
import { useSpreadsheetReviewContext } from "../SpreadsheetReviewContextProvider";
|
|
20
21
|
import { AppResponseModal } from "./AppResponseModal";
|
|
21
|
-
var valueIsTrue = function (value) {
|
|
22
|
-
var _value = (value || "").toString().toLowerCase();
|
|
23
|
-
return ["true", "yes", "1", "y", "on", "t"].includes(_value);
|
|
24
|
-
};
|
|
25
|
-
var valueIsInteger = function (value) { return /^-?[0-9]+$/.test(value + ""); };
|
|
26
|
-
var valueIsNumber = function (value) {
|
|
27
|
-
return /^-?[0-9]+(?:\.[0-9]+)?$/.test(value + "");
|
|
28
|
-
};
|
|
29
22
|
var useInitialData = function () {
|
|
30
23
|
var _a = useImporterContext(), templateHeaderMatchings = _a.templateHeaderMatchings, enumFieldValueMatchings = _a.enumFieldValueMatchings, templateHeaders = _a.templateHeaders, uploadedDataHeaders = _a.uploadedDataHeaders, uploadedData = _a.uploadedData, formatRecord = _a.formatRecord;
|
|
31
24
|
return useMemo(function () {
|
|
@@ -50,11 +43,11 @@ var useInitialData = function () {
|
|
|
50
43
|
if (columnType === FieldTypes.enum)
|
|
51
44
|
record[key] = enumFieldValueMatchings[templateLabel][value];
|
|
52
45
|
if (columnType === FieldTypes.boolean)
|
|
53
|
-
record[key] =
|
|
46
|
+
record[key] = isTrue(value);
|
|
54
47
|
if (columnType === FieldTypes.float)
|
|
55
|
-
record[key] =
|
|
48
|
+
record[key] = isFloat(value) ? parseFloat(value) : value;
|
|
56
49
|
if (columnType === FieldTypes.integer)
|
|
57
|
-
record[key] =
|
|
50
|
+
record[key] = isInteger(value) ? parseInt(value) : value;
|
|
58
51
|
});
|
|
59
52
|
return formatRecord ? formatRecord(record) : record;
|
|
60
53
|
};
|
|
@@ -71,13 +64,16 @@ var Review = function () {
|
|
|
71
64
|
var _a = useImporterContext(), templateHeaders = _a.templateHeaders, onValidateRecord = _a.onValidateRecord, onValidateBatch = _a.onValidateBatch, _b = _a.importerOptions, batchValidationDelayMs = _b.batchValidationDelayMs, batchValidationSize = _b.batchValidationSize;
|
|
72
65
|
var initialData = useInitialData();
|
|
73
66
|
var _c = useSpreadsheetReviewContext(), submittedData = _c.submittedData, appResponse = _c.appResponse, setReviewedData = _c.setReviewedData;
|
|
74
|
-
var fields =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
67
|
+
var fields = useMemo(function () {
|
|
68
|
+
return templateHeaders.map(function (templateHeader) { return ({
|
|
69
|
+
label: templateHeader.label,
|
|
70
|
+
name: templateHeader.internal_key,
|
|
71
|
+
type: FieldTypes[templateHeader.column_type] || FieldTypes.string,
|
|
72
|
+
isRequired: templateHeader.required,
|
|
73
|
+
options: templateHeader.values,
|
|
74
|
+
pattern: templateHeader.pattern,
|
|
75
|
+
}); });
|
|
76
|
+
}, [templateHeaders]);
|
|
81
77
|
var spreadsheetUI = useMemo(function () { return (_jsx(Div, { children: _jsx(Spreadsheet, __assign({ rowWidth: getSpreadsheetRowWidth(fields, 6), options: {
|
|
82
78
|
batchValidationDelayMs: batchValidationDelayMs,
|
|
83
79
|
batchValidationSize: batchValidationSize || initialData.length,
|
|
@@ -47,8 +47,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
47
47
|
};
|
|
48
48
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
49
49
|
import { createContext, useContext, useState } from "react";
|
|
50
|
-
import { getUnsubmittedData } from ".";
|
|
51
50
|
import { AppLoading } from "./common/AppLoading";
|
|
51
|
+
import { getUnsubmittedData } from "./common/utils";
|
|
52
52
|
import { useImporterContext } from "./ImporterContextProvider";
|
|
53
53
|
var SpreadsheetReviewContext = createContext({});
|
|
54
54
|
export var SpreadsheetReviewContextProvider = function (_a) {
|
|
@@ -31,7 +31,7 @@ var StepIndexWrapper = styled(Div)(templateObject_2 || (templateObject_2 = __mak
|
|
|
31
31
|
return p.isSelected && css(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n height: 32px;\n width: 32px;\n border: solid 1px ", ";\n color: ", ";\n background-color: ", ";\n "], ["\n height: 32px;\n width: 32px;\n border: solid 1px ", ";\n color: ", ";\n background-color: ", ";\n "])), function (p) { return p.theme.colors.blue500; }, function (p) { return p.theme.colors.white; }, function (p) { return p.theme.colors.blue500; });
|
|
32
32
|
});
|
|
33
33
|
var StepperWrapper = styled(Div)(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n background-color: ", ";\n user-select: none;\n border-bottom: 1px solid ", ";\n"], ["\n background-color: ", ";\n user-select: none;\n border-bottom: 1px solid ", ";\n"])), function (p) { return p.theme.colors.white; }, function (p) { return p.theme.colors.gray300; });
|
|
34
|
-
var Logo = styled.img(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n max-height: 70px;\n position: absolute;\n left: 41px;\n"], ["\n max-height: 70px;\n position: absolute;\n left: 41px;\n"])));
|
|
34
|
+
var Logo = styled.img(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n max-height: 70px;\n max-width: 300px;\n position: absolute;\n left: 41px;\n"], ["\n max-height: 70px;\n max-width: 300px;\n position: absolute;\n left: 41px;\n"])));
|
|
35
35
|
var StepIndex = function (_a) {
|
|
36
36
|
var index = _a.index, isSelected = _a.isSelected, isCompleted = _a.isCompleted;
|
|
37
37
|
return (_jsx(_Fragment, { children: isCompleted ? (_jsx(Div, __assign({ centered: true, body2: true, bold: true, mr: 8 }, { children: whiteCheckIcon }))) : (_jsx(StepIndexWrapper, __assign({ centered: true, body2: true, bold: true, mr: 8, isSelected: isSelected }, { children: index }))) }));
|
|
@@ -112,8 +112,15 @@ var Stepper = function (_a) {
|
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
114
|
var goBack = function () {
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
var isMatchingColumnValues = matcherSubstep === 1;
|
|
116
|
+
var isInMachStep = currentStepIndex === 1;
|
|
117
|
+
var shouldBackOnlyMatcherSubStep = isMatchingColumnValues && isInMachStep;
|
|
118
|
+
if (shouldBackOnlyMatcherSubStep) {
|
|
119
|
+
setMatcherSubstep(matcherSubstep - 1);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
setCurrentStepIndex(currentStepIndex - 1);
|
|
123
|
+
}
|
|
117
124
|
};
|
|
118
125
|
var skipStep = function () {
|
|
119
126
|
setStep();
|
|
@@ -61,7 +61,10 @@ var ImporterDropzone = function () {
|
|
|
61
61
|
reader.onload = function (event) {
|
|
62
62
|
var results = event.target.result;
|
|
63
63
|
var getDataFromFile = function (fileResults) {
|
|
64
|
-
var workBook = XLSX.read(fileResults, {
|
|
64
|
+
var workBook = XLSX.read(fileResults, {
|
|
65
|
+
type: "array",
|
|
66
|
+
raw: true,
|
|
67
|
+
});
|
|
65
68
|
var workBookName = workBook.SheetNames[0];
|
|
66
69
|
var sheet = workBook.Sheets[workBookName];
|
|
67
70
|
return XLSX.utils.sheet_to_json(sheet, { header: 1 });
|