Coder Social home page Coder Social logo

benwinding / react-admin-import-csv Goto Github PK

View Code? Open in Web Editor NEW
129.0 3.0 45.0 29.8 MB

A csv file import button for react-admin

Home Page: https://benwinding.github.io/react-admin-import-csv

License: MIT License

JavaScript 0.61% TypeScript 99.39%
react-admin react csv bulkimport

react-admin-import-csv's Introduction

react-admin-import-csv

NPM Version Downloads/week License Github Issues Build and Publish Code Coverage

CSV import button for the react-admin framework.

image

Usage

Simply import the button into a toolbar, like so:

Basic Import Action Only

import {
  Datagrid,
  List,
  TextField,
  RichTextField,
  TopToolbar,
} from "react-admin";
import { ImportButton } from "react-admin-import-csv";
import { CreateButton } from "ra-ui-materialui";

const ListActions = (props) => {
  const { className, basePath } = props;
  return (
    <TopToolbar className={className}>
      <CreateButton basePath={basePath} />
      <ImportButton {...props} />
    </TopToolbar>
  );
};
export const PostList = (props) => (
  <List {...props} filters={<PostFilter />} actions={<ListActions />}>
    <Datagrid>
      <TextField source="title" />
      <RichTextField source="body" />
      ...
    </Datagrid>
  </List>
);

Export/Import Actions

import {
  Datagrid,
  List,
  TextField,
  RichTextField,
  TopToolbar,
} from "react-admin";
import { ImportButton } from "react-admin-import-csv";
import { CreateButton, ExportButton } from "ra-ui-materialui";

const ListActions = (props) => {
  const {
    className,
    basePath,
    total,
    resource,
    currentSort,
    filterValues,
    exporter,
  } = props;
  return (
    <TopToolbar className={className}>
      <CreateButton basePath={basePath} />
      <ExportButton
        disabled={total === 0}
        resource={resource}
        sort={currentSort}
        filter={filterValues}
        exporter={exporter}
      />
      <ImportButton {...props} />
    </TopToolbar>
  );
};
export const PostList = (props) => (
  <List {...props} filters={<PostFilter />} actions={<ListActions />}>
    <Datagrid>
      <TextField source="title" />
      <RichTextField source="body" />
      ...
    </Datagrid>
  </List>
);

Configuration Options

// All configuration options are optional
const config: ImportConfig = {
  // Enable logging
  logging?: boolean;
  // Disable the attempt to use "createMany", will instead just use "create" calls
  disableCreateMany?: boolean,
  // Disable the attempt to use "updateMany", will instead just use "update" calls
  disableUpdateMany?: boolean,
  // Disable the attempt to use "getMany", will instead just use "getSingle" calls
  disableGetMany?: boolean,
  // Disable "import new" button
  disableImportNew?: boolean;
  // Disable "import overwrite" button
  disableImportOverwrite?: boolean;
  // A function to translate the CSV rows on import
  preCommitCallback?: (action: "create" | "overwrite", values: any[]) => Promise<any[]>;
  // A function to handle row errors after import
  postCommitCallback?: (error: any) => void;
  // Transform rows before anything is sent to dataprovider
  transformRows?: (csvRows: any[]) => Promise<any[]>;
  // Async function to Validate a row, reject the promise if it's not valid
  validateRow?: (csvRowItem: any) => Promise<void>;
  // Any option from the "papaparse" library
  parseConfig?: {
    // For all options see: https://www.papaparse.com/docs#config
  }
}
<ImportButton {...props} {...config}/>

Handle id fields which might be numbers

Use the paparse configuration option dynamicTyping

const importOptions = {
  parseConfig?: {
    // For all options see: https://www.papaparse.com/docs#config
    dynamicTyping: true
  }
}

Reducing Requests (.createMany() and .updateMany())

Your dataprovider will need to implement the .createMany() method in order to reduce requests to your backend. If it doesn't exist, it will fallback to calling .create() on all items, as shown below (same goes for .update()):

Name Special Method Fallback Method
Creating from CSV .createMany() .create()
Updating from CSV .updateManyArray() .update()
Checking which exist .getMany() .getSingle()

Note: You can disable this feature setting disableCreateMany: true or disableUpdateMany: true in the configuration.

Interfaces

The dataprovider should accept these param interfaces for the bulk create/update methods.

export interface UpdateManyArrayParams {
  ids: Identifier[];
  data: any[];
}
export interface CreateManyParams {
  data: any[];
}

Example Implementation

Here's a quick example of how to implement .createMany() and .updateMany() in your dataprovider:

// Must be react-admin 3.x
const dataProviderWrapped = {
  ...dataProvider, // <- Your data provider
  createMany: async (resource, params) => {
    const items = params.data;
    // Handle create many here
  },
  updateMany: async (resource, params) => {
    const items = params.data;
    const idsToUpdate = params.ids;
    // Handle update many here
  }
}

// Pass into to other parts of the system as normal
return (
  <Admin dataProvider={dataProviderWrapped}

Translation i18n

This package uses react-admin's global i18n translation. Below is an example on how to set it up with this package.

Current supported languages (submit a PR if you'd like to add a language):

  • English (en)
  • Spanish (es)
  • Chinese (zh)
  • German (de)
  • French (fr)
  • Brazilian Portuguese (ptBR)
  • Russian (ru)
  • Dutch (nl)

Example (i18nProvider)

import { resolveBrowserLocale, useLocale } from "react-admin";
import polyglotI18nProvider from "ra-i18n-polyglot";
import englishMessages from "ra-language-english";
// This package's translations
import * as domainMessages from "react-admin-import-csv/lib/i18n";

// Select locale based on react-admin OR browser
const locale = useLocale() || resolveBrowserLocale();
// Create messages object
const messages = {
  // Delete languages you don't need
  en: { ...englishMessages, ...domainMessages.en },
  zh: { ...chineseMessages, ...domainMessages.zh },
  es: { ...spanishMessages, ...domainMessages.es },
};
// Create polyglot provider
const i18nProvider = polyglotI18nProvider(
  (locale) => (messages[locale] ? messages[locale] : messages.en),
  locale
);

// declare prop in Admin component
<Admin dataProvider={dataProvider} i18nProvider={i18nProvider}>

More information on this setup here

Development

If you'd like to develop on react-admin-import-csv do the following.

Local install

  • git clone https://github.com/benwinding/react-admin-import-csv/
  • cd react-admin-import-csv
  • yarn

Tests

  • yarn test # in root folder

Run demo

Open another terminal

  • yarn build-watch

Open another terminal and goto the demo folder

  • yarn start

react-admin-import-csv's People

Contributors

andresgarciadelahuerta avatar axehomeyg avatar benwinding avatar dependabot[bot] avatar fernandokga avatar fkowal avatar fperrais avatar hardingmatt avatar hmassonn avatar joachimbrasier avatar konkretnekosteczki avatar kristelvdakker avatar lodwrot avatar mananiux avatar mattia-longhin avatar morz avatar odonno avatar ptflores1 avatar sebcramer avatar songkeith avatar steventilator avatar tiaanswart avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

react-admin-import-csv's Issues

Not working with app cache

If I use the application cache suggestion from https://marmelab.com/react-admin/Caching.html#application-cache , the import button do not work

const cacheDataProviderProxy = (dataProvider, duration =  2 * 60 * 1000) =>
    new Proxy(dataProvider, {
        get: (target, name) => (resource, params) => {
            if (name === 'getOne' || name === 'getMany' || name === 'getList') {
                return dataProvider[name](resource, params).then(response => {
                    const validUntil = new Date();
                    validUntil.setTime(validUntil.getTime() + duration);
                    response.validUntil = validUntil;
                    return response;
                });
            }
            return dataProvider[name](resource, params);
        },
    });

export default cacheDataProviderProxy(myDataProvider);

With the cache active, the import button do not make an API call, it stops as the image bellow:
image

If I delete it and use the myDataProvider, it works fine.

Is there a solution?

Thanks!!

Colliding ids for disableGetMany option

I've been trying to use the module with react-admin-firebase, however react-admin-firbease does return objects for the getMany calls (with just ids) even if those do not exist, which forced me to use the getOne instead.

However, It seems that when resource is successfully obtained via getOne the id is marked as non colliding, and when there is an error when querying the resource it is marked as colliding, which is the exact opposite of how it should work.

return dataProvider.getOne(resourceName, {id})
.then(_ => undefined)
.catch(_ => id);

Simply replacing the return value of then with return value of catch and vice verse should fix the issue.

Some suggestions

Hi @benwinding

I just made some PRs in order to resolve some issues I had in the last weeks. I hope you'll like the improvements.

I have some other suggestions that can be discussed here if you want.

Suggestions

console.log({ values });

I suppose that all console.log should be forbidden, right?

values.map(value => dataProvider.create(resource, { data: value }))

I used to dispatch an action crudUpdateMany in order to save that in the redux store and then it will trigger an UPDATE_MANY event to the according data provider. You, otherwise, you use directly the data provider for each item. I am not sure what is the best use of this ATM. Otherwise, I can see there is 2 kind of operations:

  • UPDATE if the primary key does not exist in the list
  • CREATE if the primary key already exist

It can be a little tricky but I believe we should allow the developer to choose the action to execute in those cases, don't you think? Like adding a new prop.

Also, we manage papaparse props ourselves. That could be interesting to set default props and then let the developers override the props based on their own needs. Maybe by reusing the same standard of react-admin. They use an exporter prop, we could have an importer prop.

change encoding

Hi friends
When I import a CSV file that contains Persian letters, the letters will be converted to "????". I think the problem is in encoding.
I want to change the CSV encoding when uploading a file. How can I do this?

Unexpected behaviour on import with dynamicTyping

Sorry to disturb you again, but I've found this:

If you change your example posts.js to:

const ListActions = (props) => {
  const {
    className,
    basePath,
    total,
    resource,
    currentSort,
    filterValues,
    exporter,
  } = props;
  const config = {
    logging: true,
    validateRow: async (row) => {
      if (row.id) {
        // throw new Error("AAAA");
      }
    },
  };
  return (
    <TopToolbar className={className}>
      <CreateButton basePath={basePath} />
      <ExportButton
        disabled={total === 0}
        resource={resource}
        sort={currentSort}
        filter={filterValues}
        exporter={exporter}
      />
      <ImportButton {...props} {...config} parseConfig={{dynamicTyping: true}}/>
    </TopToolbar>
  );
};

i.e. delete postCommitCallback and add this parseConfig, when you import data with conflicting ids, it will not show the dialog but quit directly. Is it an expected behaviour? Thanks.

Import functionality with row by row is updating wrong record

when I tried importing csv file
posts.csv

and selected "Let me decide for each row"
Current Record

Now you can see in console current record is with ID "4" But when I click "Replace the row ID =4" button, Code replaces record with ID=3
replaced-wrong-record

This happens with all the records when tried to replace or add new.

Minimize HTTP requests

Hi, and thanks for sharing this module, it's really helpful.

I have a concern with the way you send the imported records to the backend (using a list of create calls). This is not efficient from a network point of view, and can even be seen as a DoS from the backend.

May I suggest you call a new verb on the dataProvider (something like createMany), and leave the implementation to the developers? That way, if their API allows a single call to create many records, they can optimize the call.

I also have a concern about the create / update choice un the UI. In my opinion, the button should be smarter, and detect the records that are already present (using a getMany), then update those and create the remaining ones. The option in the dialog should disappear, and only be shown if necessary (just like the file copy dialog in your system asks for a confirmation to ignore, update, or stop in that case).

Finally, I suggest that clicking on the "import" button opens the file selector right away. The file format (csv/tsv) can be enforced by a type attribute in the file input, and the presence of an id column can be checked a posteriori.

These limitations are kind of blockers for me. Is there a chance that you implement these in your module, or should I redevelop another module on my side?

Remove Direct Dependency on "ra-language-XXXX" packages

Currently this package directly depends on ra-language-chinese and ra-language-spanish

"dependencies": {
"papaparse": "4.x",
"ra-language-chinese": "^2.0.5",
"ra-language-spanish": "1.x",
"ramda": "^0.27.0"
},

A better way would be to implicitly depend on the locale language that the parent project needs.

So, I'm wondering if we can try and remove this dependency here somehow... so more languages can be added by the user somehow...

import englishMessages from 'ra-language-english';
import spanishMessages from 'ra-language-spanish';
import chineseMessages from 'ra-language-chinese';

Anyone have any ideas?

preCommitCallback is incorrectly called with a first argument of `create` for an update

In file uploader.ts, the preCommitCallback is incorrectly called with a first argument of create for an update, while it should be overwrite per the type definition

file uploader.ts:

export async function update(
  logging: boolean,
  dataProvider: DataProvider,
  resource: string,
  values: any[],
  preCommitCallback: PrecommitCallback,
  postCommitCallback: ErrorCallback,
) {
  const parsedValues = preCommitCallback ? await preCommitCallback('create', values) : values;

file config.interface.ts:

export type PrecommitCallback = (action: 'create' | 'overwrite', values: any[]) => Promise<any[]>;

Unable to add Filters when using the Import button

The Import button on its own works fine but the Filter is not visible, so then I followed Issue #1 but then I started getting error.

In my code I have:

const QuestionListActions = (props) => {
	const { className, basePath, total, resource, currentSort, filterValues, exporter } = props;
	const config = {
		logging: true,
		validateRow: async (row) => {
			if (row.id) {
				// throw new Error("AAAA");
			}
		},
		postCommitCallback: (reportItems) => {
			console.log("reportItems", { reportItems });
		},
		// disableImportNew: true,
		// disableImportOverwrite: true,
	};
	return (
		<TopToolbar className={className}>
			<QuestionFilter {...props}/>
			<CreateButton basePath={basePath} />
			<ExportButton
				disabled={total === 0}
				resource={resource}
				sort={currentSort}
				filter={filterValues}
				exporter={exporter}
			/>
			<ImportButton
				{...props}
				{...config}
				parseConfig={{ dynamicTyping: true }}
			/>
		</TopToolbar>
	);
};

const QuestionFilter = (props) => (
	<Filter {...props}>
		<TextInput label='Search' source='q' alwaysOn />
		<ReferenceInput
			label='User'
			source='userId'
			reference='users'
			allowEmpty
		>
			<SelectInput optionText='name' />
		</ReferenceInput>
	</Filter>
);

export const QuestionList = (props) => (
	<List {...props} actions={<QuestionListActions />}>
		<Datagrid>
			<TextField source='id' />
			<ReferenceField label='User' source='userId' reference='users'>
				<TextField source='name' />
			</ReferenceField>
			<TextField source='question' />
			<TextField source='answer' />
			<EditButton base='/questions' />
			<ShowButton base='/questions' />
			<DeleteButton base='/questions' />
		</Datagrid>
	</List>
);

Below is the error log that I am getting
image

TypeError: Cannot read property 'filter' of undefined

    at EnhancedFilterForm (http://localhost:3000/static/js/vendors~main.chunk.js:268632:31)
    at Filter (http://localhost:3000/static/js/vendors~main.chunk.js:268189:17)
    at div
    at Toolbar (http://localhost:3000/static/js/vendors~main.chunk.js:31883:23)
    at WithStyles(ForwardRef(Toolbar)) (http://localhost:3000/static/js/vendors~main.chunk.js:180170:31)
    at TopToolbar (http://localhost:3000/static/js/vendors~main.chunk.js:264811:25)
    at ListActions (http://localhost:3000/static/js/main.chunk.js:451:5)
    at div
    at Toolbar (http://localhost:3000/static/js/vendors~main.chunk.js:31883:23)
    at WithStyles(ForwardRef(Toolbar)) (http://localhost:3000/static/js/vendors~main.chunk.js:180170:31)
    at ListToolbar (http://localhost:3000/static/js/vendors~main.chunk.js:265783:31)
    at div
    at ListView (http://localhost:3000/static/js/vendors~main.chunk.js:265877:23)
    at ListContextProvider (http://localhost:3000/static/js/vendors~main.chunk.js:221843:18)
    at List (http://localhost:3000/static/js/vendors~main.chunk.js:265460:79)
    at QuestionList

Question: Add additional field to CSV records before posting

Hello,

First of all, thank you for this feature! It really saves a lot of time.

Here is my use case:

I am importing a csv file that looks like this:

title, id
title1, 1
title2, 2

I would like to add an extra CSV field before posting, so that the post request payload looks like this:

{
   title: title1,
   id: 1,
   company: 1,
},
{
  title: title2,
  id: 2, 
  company: 1,
},

The additional field is always a constant value that I need to send to my API endpoint to perform the post request.

Is it possible to achieve this with transform rows in config? Has anyone tried to do this before?

[Howto] Specific type (boolean, etc.)

Hi,

Nice work!! You saved me time! I'm new in React and I'm working on a side project to develop a small admin interface above my own API.

I have a small question: is it possible to import via CSV specific type, like boolean ?

Thank you !

Improvement: pass more params into `validateRow` for improved row validation

In file import-controller.ts, function CheckCSVValidation, change from:

await Promise.all(csvValues.map((v) => validateRow(v)));

to:

await Promise.all(csvValues.map(validateRow));

so user-provided validation function has access to row index to inform user which row is failing validation, and even the full array of records (3rd arg from array.map) to allow cross-record validation in a data set.

preCommitCallback running to late

Hi

I wanted to discuss an issue i ran into to hopefully find a solution

The issue is that the preCommitCallback is called to late in the pipeline

My business case is that Business guy goes to react-admin exports an CSV
fills out some missing information in 1 column and the imports the data back into the system

ie. He export 1000 rows, he fills only 50 and the uses the import CSV

So during import id like the pipeline to looks like

parseCSV -> preCommitCallback -> collisionDetection ....

With the currency pipeline
parseCSV -> collsionDetection -> ... -> very end preCommitCallback

The Override/replace Modal shows 950 rows that are going to be ignored

My suggestion would be

  1. add a config: {transform?: (csvRows[]) => csvRows[]) and invoke this right after GetCSVItems
  2. move th preCommitCallback invocation from being the last step to right after GetCSVItems

Adding this transformation after GetCsvItems would also greatly limits the data fetched/send to GetIdsColliding

I am also open to hearing other ideas

array of one element

I transform some data width transform func in parse config to array of numbers (for example [123,123,123,123])
If array has 2 and more elements, it transmitted to dataProvider normally
But if array has one element (for example [123]), in transmitted to dataProvider as single number 123

Is updateMany the right method to call?

Hello,

First thank you very much for the work, this feature is awesome.
I came accros something while getting this to work for me.

Context

My use case is to only allow updating records, so I used the disableImportNew: true option.
When updating records, react-admin-import-csv calls the dataProvider method updateMany.

I realised that the format of the data sent to the updateMany method of the data provider is as follow:

{
    ids : [1,2],
    data : [
        {
            id: 1,
            text: "Some text"
        },
        {
            id: 2,
            text: "Some other text"
        }
    ]
}

I am not sure this is the intended format for updateMany in React admin.
From the documentation it seems like updateMany is more intended to update many records with the same data, rather than a different change for each record. (see https://marmelab.com/react-admin/DataProviders.html#request-format and the example implementation).

In my case, I am using feathersjs for my API, and using the ra-data-feathers module to create the react-admin dataProvider.
That module is definetly written in a way that updateMany expects the same change for many records.

//extract from ra-data-feathers/src/restClient.js line 94 - 102
case UPDATE_MANY:
        if (usePatch) {
          const dataPatch = params.previousData
            ? diff(params.previousData, params.data) : params.data;
          return Promise.all(params.ids.map(id => (service.patch(id, dataPatch)))); //--> Send the same data to all ids
        }
        const dataUpdate = (idKey !== defaultIdKey)
          ? deleteProp(params.data, defaultIdKey) : params.data;
        return Promise.all(params.ids.map(id => (service.update(id, dataUpdate))));

So in the very specific case of feathersjs, I could get it to work by writting a before hook that would isolate the id in the data.

My question:

Is updateMany the appropriate dataProvider method to call here, or should update be called for every single record to update instead?
I know this can potentially increase the number of requests, but may be more in line with the intended use of the methods?

In the case of feathersjs and ra-data-feathers, the updateMany method triggers 1 patch call per id anyway.
I don't know how other dataProviders are written, so it might not be an issue for others.

postCommitCallback always receives empty array for creation

<ImportButton
                resource={resource}
                postCommitCallback={reportItems =>
                    console.log('reportItems', reportItems)
                }
            />

Always logs an empty array ([]).

I suspect that's because createInDataProvider never updates the reportItems in

async function createInDataProvider(
logging: boolean,
dataProvider: DataProvider,
resource: string,
values: any[]
): Promise<ReportItem[]> {
logger.setEnabled(logging);
logger.log("addInDataProvider", { dataProvider, resource, values });
const reportItems: ReportItem[] = [];
try {
await dataProvider.createMany(resource, { data: values });
} catch (error) {
const shouldUseFallback = error.toString().includes("Unknown dataProvider");
if (shouldUseFallback) {
logger.log(
"addInDataProvider",
"createMany not found on provider: using fallback"
);
try {
await createInDataProviderFallback(dataProvider, resource, values);
} catch (error) {
logger.error("addInDataProvider", error);
}
}
}
return reportItems;
}

in18provider prop

Hello! Could you add an 'i18nProvider' prop to forward the custom provider?

Disable "Import As New"?

I have an app where users should not be able to create new records - only update existing records.

I tried to find a way to disable the Import As New feature using REFs but wasn't able to.

Is there an easier way to do what I want to do here?

Create, Import, Export and Filter

Hi,
Congratulations on your work!!

In my code I have:

const ListActions = props => {
  const { 
    className, 
    basePath, 
    total, 
    resource, 
    currentSort, 
    filterValues, 
    exporter 
  } = props;
  return (
    <TopToolbar className={className}>
      <CreateButton basePath={basePath} />
      <ExportButton
        disabled={total === 0}
        resource={resource}
        sort={currentSort}
        filter={filterValues}
        exporter={exporter}
      />
      <ImportButton {...props} />
    </TopToolbar>
  );
};

const UserFilter = (props) => (
  <Filter {...props}>
    <TextInput label="Search by name" source="name" alwaysOn />
    <ReferenceInput label="Country" source="country" reference="countries" allowEmpty>
      <SelectInput optionText="country" />
    </ReferenceInput>
    <ReferenceInput label="Community" source="community" reference="communities" allowEmpty>
      <SelectInput optionText="community" />
    </ReferenceInput>
  </Filter>
);

export const UserList = (props) => (
  <List {...props} filter={{ createdby: currentUser()}} filters={<UserFilter />} filterDefaultValues={{ createdby: currentUser()}} actions={<ListActions/>}>
    <Datagrid>
      <TextField source="name" />
      <TextField source="username" />
      <TextField source="sex" />
      <ImageField source="avatar.src" title="avatar" />
      <ReferenceField source="country" reference="countries" allowEmpty>
        <TextField source="country" />
      </ReferenceField>
      <ReferenceField source="community" reference="communities" allowEmpty>
        <TextField source="community" />
      </ReferenceField>
      <DateField source="birhtday" elStyle={{ color: 'red' }} />
      <TextField source="phone" />
      <EmailField source="email" />
      <UrlField source="web" />
      <BooleanField source="isAdmin" />
      <ShowButton label="" />
      <EditButton label="" />
      <DeleteButton label="" redirect={false}/>
    </Datagrid>
  </List>
);

But, I can not see my Filter button....

Can you help me, please. Regards

Missing keys

I get the following warnings when I try to use the import button:

Warning: Missing translation for key: "csv.buttonMain.label" index.js:1
Warning: Missing translation for key: "csv.buttonMain.tooltip" index.js:1
Warning: Missing translation for key: "csv.dialogImport.title" index.js:1
Warning: Missing translation for key: "csv.dialogCommon.subtitle" index.js:1
Warning: Missing translation for key: "csv.dialogDecide.title" index.js:1
Warning: Missing translation for key: "csv.dialogCommon.subtitle" index.js:1
Warning: Missing translation for key: "csv.dialogCommon.conflictCount" index.js:1
Warning: Missing translation for key: "csv.dialogDecide.buttons.replaceRow" index.js:1
Warning: Missing translation for key: "csv.dialogDecide.buttons.addAsNewRow"

I'm using react-admin version 3.9.6 and the latest version of react-admin-import.csv

The response to 'getMany' must be like { data : [...] }

Hi there!

I'm having this issue, I'm not sure if I'm missing something, but here I go:

When I upload a CSV file, it looks like is making a get request filtering some data, and I'm dealing with this error:

The response to 'getMany' must be like { data : [...] }, but the received data is not an array. The dataProvider is probably wrong for 'getMany'

Screen Shot 2021-09-20 at 18 53 05

This is happening even though I'm receiving the data in the correct way:

Screen Shot 2021-09-20 at 18 55 32

Here in Postman:

Screen Shot 2021-09-20 at 18 58 00

I hope someone can help me out with this, thanks a lot!

Unsupported fetch action type createMany

When I tried to use the import function, I am seeing:

Unsupported fetch action type createMany

could you please provide an example of how to implement createMany

Error on GetIdsColliding when importing new rows

Hello there,

I'm wondering if that's the normal behaviour :

  • If I just export and re-import my CSV, I got my rows updated, that's well!
  • If I export a CSV, edit it, add 1 row with a non existant ID, I'm going to get an error on GetIdsColliding because the request return a 404 not found.

I'm using API Platform for the data provider. Using your demo, it'll never happen because there's fake data (which not call any API)

How can I handle that behaviour to don't throw any error and create the concerned row ?
It is not automatic ? Should I use the callback from the ImportConfig in order to create it by myself ?

I'm pretty sure that's not the goal or desired behaviour, I'm wrong ?

Any help would be really appreciated, thanks in advance!

image

Validation for CSV Files and Error Handling

Currently there are no validators available for processing the CSV file, moreover if any of the data has errors leading to one of the row not being inserted, the dialog box just haults on loading.

Handler for skip seems to create a new record still

Comparing these 2 handlers in import-csv-button.tsx it appears the skip handler creates a new record still.
Should createRows([val]) not be deleted in handleAskDecideSkip?

  const handleAskDecideAddAsNew = async () => {
    logger.log('handleAskDecideAddAsNew');
    const val = nextConflicting();
    if (!val) {
      return handleClose();
    }
    delete val.id;
    await createRows([val]);
  };

  const handleAskDecideSkip = async () => {
    logger.log('handleAskDecideSkip');
    const val = nextConflicting();
    if (!val) {
      return handleClose();
    }
    createRows([val]);
  };

[QUESTION] How to send a field with an array of ids

Hi!
First, thanks for the great package!

I'm trying to send an csv and almost everything is fine, but one field (type_ids) is an array, which need to be send like:
"client_id":1,"type_ids":[3,2,1]

But when I import, it's send like:
"client_id":1,"type_ids":"3,2,1"

It sends the field as an string.
In my csv it is: 1;2;3

My config of the importer is:
<ImportButton {...props} parseConfig={{ dynamicTyping: true }} />

How can I change the format?

Best regards!

Customizable/override collistion detection code

Hey

I ran into an issue with collision detection

I export data using the build in Export the Import using your plugin

And here is what happens

logger.log("Has colliding ids?", { hasCollidingIds, collidingIds });
shows that all Ids this makes sense because i exported the data from the system

So this works fine

But the

logger.log("Importing items which arent colliding", {
        csvItemsNotColliding,
      });

Sadly show the very same Ids as the colliding Ids

And the end result is the the same set of data is passed to create and then to update

So i'd like to propose adding a config: {partition: (csv[]) => [[rows2Create], [rows2Update]]

The end goal here would be to give the user control of what goes into create and what goes to update

CSV with empty column Error Handling

When you try to import CSV with an empty column it throws an error in logging but there is no way to catch that error. I have tried to use papaparse skipEmpty lines option but that does not work either. Also tried validRow but I think the error is before executing that function. It would be great if you can help me out here. Thanks in advance.
Screenshot 2022-04-22 at 1 36 40 PM

preCommitCallback is ignored due to hooks setting values asynchronously

Hello,

First of all, thank you for your work!

I think that there is an issue with preCommitCallback. You call setValues with the result of preCommitCallback and then you use the content of values variable directly.

This does not work because you cannot trust setValues to synchronously change values. Your are providing the old values to the create function.

if (preCommitCallback) setValues(preCommitCallback('create', values));

Inject record data into transformRows callback

Hi there!

I have a Show component for a 'Parent' resource and I want to import a CSV which contains entries of a 'Child' resource. Now I need to reference the currently displayed Parent ID in a field for every imported Child.
I understand that I can add static information in the transformRows callback but how can I possibly inject the currently displayed Parent record into the function?

Thank you in advance for any solution or hint in the right direction!

const configuration: ImportConfig = {
  // ...
  transformRows: (csvRows) => {
    let rows = [];
    csvRows.map((row) => {
      row.parent_id = parent.id; // this is where I need the injected data
      rows.push(row);
    });
    return rows;
  },
  // ...
}

export const ParentShow = (props) => (
    <Show {...props}>
        <TabbedShowLayout>
            <Tab label="Parent Details">
                <TextField source="parentdetail1" />
                <TextField source="parentdetail2" />
            </Tab>
            <Tab label="Children" path="children">
                <ImportButton {...props} {...configuration} />
                <ReferenceManyField reference="children" target="parent_id">
                    <Datagrid>
                        <TextField source="childdetail" />
                    </Datagrid>
                </ReferenceManyField>
            </Tab>
        </TabbedShowLayout>
    </Show>
);

Missing lib/ folder

Hello,

I tried updating to 1.0.3 from 0.2.18 but I'm then unable to resolve imports due to a missing lib/ folder.

Module not found: Can't resolve 'react-admin-import-csv' in '...'

Here's the file structure in node_modules/react-admin-import-csv in 0.2.18 (and 0.2.20).
image

Here's the file structure in node_modules/react-admin-import-csv in 1.0.3 (and 1.0.2).
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.