Import
Ajouter ou mettre à jour des fiches depuis un fichier CSV ou du texte collé, avec un écran de correspondance des colonnes, une vérification et une écriture groupée côté serveur.
Données › Importer permet d'ajouter ou de mettre à jour des fiches depuis un fichier CSV ou du texte collé. La table gère tout l'écran : lecture du fichier, correspondance de ses colonnes avec celles de la table, conversion des valeurs, vérification et progression. Votre application se contente d'enregistrer les lignes, via ses actions create et update existantes ou une écriture groupée côté serveur. React et Vue se comportent de la même façon.
Importer apparaît dans le menu Données, entre Exporter et Connecter, lorsque la table peut créer des lignes (allowCreate avec actions.create), les modifier (allowEdit avec actions.update) ou importer en masse (actions.import.importRows). Mettez table.import: false pour le masquer ; voir Configuration.
Ce que voit l'utilisateur
Choisir un fichier
La première étape propose une zone de dépôt et Choisir un fichier pour un fichier .csv, .tsv ou .txt, ainsi qu'une zone de texte pour coller du CSV, par exemple des cellules copiées depuis un tableur. Les sources déclarées par votre application suivent, comme un CRM ou une autre table ; voir Autres sources.
Encodage : le fichier est lu en UTF-8, ou en Windows-1252 lorsqu'il n'est pas de l'UTF-8 valide (anciens exports Excel). Une marque d'ordre des octets (BOM) est ignorée.
Séparateur : virgule, point-virgule, tabulation ou barre verticale, détecté à partir des premières lignes. La liste Séparateur affiche celui qui a été détecté et permet d'en imposer un autre.
En-têtes : La première ligne contient les en-têtes est cochée par défaut. Décochez-la lorsque le fichier commence directement par des données ; ses colonnes s'appellent alors « Column 1 », « Column 2 »…
Les valeurs entre guillemets, les guillemets échappés, les retours à la ligne entre guillemets et les fins de ligne CRLF comme LF suivent la RFC 4180. Les lignes vides sont ignorées.
Associer les colonnes
Chaque colonne du fichier a sa ligne, avec une valeur d'exemple (« ex. Alpha launch ») et une liste des colonnes de la table plus Ignorer. La table propose une correspondance pour chaque colonne :
Par le nom : l'en-tête du fichier est comparé à l'en-tête et à l'identifiant de chaque colonne, sans tenir compte des accents, de la casse, des espaces, des
-et des_. Lorsque deux colonnes portent le même nom, celle dont le type convient aux valeurs l'emporte.Par les valeurs : un en-tête qu'aucun nom ne reconnaît va à la colonne encore libre qui convertit le plus de ses 20 premières valeurs, au moins 80 % d'entre elles. Une colonne texte ne l'emporte jamais sur les seules valeurs, et une égalité laisse la colonne du fichier ignorée.
Sinon, la colonne est ignorée.
Chaque correspondance peut être modifiée à la main. Une colonne de la table reçoit une seule colonne du fichier : choisir une colonne déjà prise la déplace et ignore l'autre colonne du fichier. Un badge à côté de chaque ligne indique le type de la colonne, ou combien de ses 20 premières valeurs ne sont pas convertibles (« 3 non convertibles »).
Associer aux fiches existantes par choisit la colonne clé. Les lignes du fichier dont la clé correspond à une fiche la mettent à jour ; les autres sont ajoutées. Par défaut, c'est Ne pas associer (tout ajouter), sauf si une colonne associée est un identifiant (id, key ou Yayaw ID).
L'Aperçu montre les cinq premières lignes telles qu'elles seront enregistrées. Une cellule invalide est surlignée, et son erreur s'affiche au survol.
Vérifier et importer
Vérifier recherche les clés, puis indique combien de lignes seront ajoutées, mises à jour ou sont en erreur (« 2 à ajouter », « 1 à mettre à jour », « 2 lignes en erreur »), avec les trois premières erreurs, par exemple « Ligne 5, Statut : ne fait pas partie des options ». Les lignes sont comptées comme celles du fichier, l'en-tête étant la ligne 1.
Ignorer les lignes en erreur est coché par défaut : les autres lignes sont importées. Le décocher bloque Importer jusqu'à ce que le fichier soit corrigé. Retour revient à la correspondance et Annuler ferme l'écran sans rien écrire.
Importer écrit les lignes par lots avec une barre de progression (« Import… 50/120 »). Arrêter interrompt l'import entre deux lots. Une ligne en échec n'arrête pas les autres ; seule une erreur de connexion ou d'autorisation arrête l'import. Le résultat indique les lignes ajoutées, mises à jour et en échec, avec les premiers échecs, puis Terminé ou Importer un autre fichier. La table se rafraîchit dès qu'une ligne a été écrite.
Sur téléphone, l'écran s'ouvre dans le tiroir Données et chaque liste s'ouvre en plein écran.
Conversion des valeurs
Chaque cellule est convertie selon le type de la colonne qui la reçoit :
| Type de colonne | Valeurs acceptées |
|---|---|
| Nombre, devise, pourcentage, note | Virgules ou points décimaux et séparateurs de milliers (1 234,5, 1,234.5, 1.234.567), symboles monétaires, %, négatifs entre parenthèses, notation scientifique. Le séparateur décimal du numberFormat de la colonne l'emporte ; sinon, un séparateur ambigu isolé, comme dans 1,234, suit la langue. Une valeur en % est divisée par 100 lorsque la colonne stocke les pourcentages en fractions. |
| Date, date et heure | Dates ISO, avec ou sans heure ni fuseau ; jj/mm/aaaa ou mm/jj/aaaa (avec /, . ou -), détecté par colonne : une première partie supérieure à 12 indique le jour en premier, une deuxième partie supérieure à 12 le mois en premier, sinon la langue décide ; numéros de série Excel ; dates en texte comme March 4, 2026. Les dates sont écrites au format AAAA-MM-JJ, avec l'heure pour les colonnes date et heure. |
| Case à cocher | true/false, yes/no, y/n, oui/non, vrai/faux, 1/0, x, on/off, checked/unchecked, ✓. |
| Liste, statut | La valeur ou le libellé d'une option, sans tenir compte des accents ni de la casse. |
| Liste multiple, étiquettes | Plusieurs options séparées par des points-virgules, des virgules, des barres verticales ou des retours à la ligne. |
| URL, image | Liens http, https, mailto et tel ; www. reçoit https://. |
| Une adresse e-mail. | |
| JSON | Du JSON valide. |
| Texte et autres types | Le texte tel quel. |
Une cellule vide est vide (
null). Une colonne obligatoire la refuse sur les lignes qui créent une fiche. Sur les lignes qui mettent à jour une fiche, les cellules vides laissent la valeur existante inchangée.Une option de liste inconnue est une erreur, sauf si
actions.import.allowNewOptionsvauttrue: la valeur est alors conservée comme nouvelle option.Une valeur de clé répétée dans le fichier est une erreur sur ses lignes suivantes : un fichier ne met jamais à jour deux fois la même fiche.
Les colonnes de sélection, d'actions et personnalisées ne sont jamais proposées.
L'écran de correspondance des colonnes
L'étape de correspondance est un composant réutilisable, ColumnMapping (components/toolbar/column-mapping.tsx en React, components/toolbar/ColumnMapping.vue en Vue). L'écran d'envoi des destinations de connexion l'utilise aussi, dans l'autre sens : des colonnes de la table vers les champs de la cible. Les deux écrans associent les noms selon les mêmes règles, depuis le module partagé field-matching.ts.
Actions d'import
Sans aucune configuration, un import CSV compare les clés aux fiches de la table et écrit chaque ligne via create ou update. Déclarez import dans les actions de la table (getTableActions en React, get-table-actions en Vue) pour changer ce comportement. Chaque champ est facultatif :
import: {
csv: true, // proposer les fichiers CSV et le texte collé (true par défaut)
sources: [], // autres sources, après le CSV
importRows: (batch) => importProjects(batch), // écriture groupée côté serveur
lookup: ({ columnId, keys }) => findProjectIds({ columnId, keys }), // recherche des clés
allowNewOptions: false, // conserver les options de liste inconnues
batchSize: 50, // lignes par écriture (50 par défaut)
},importRows(batch, context)reçoit jusqu'àbatchSizelignes :{ creates: { rowIndex, values }[], updates: { rowIndex, values, id }[] }.valuescontient les valeurs converties, indexées par identifiant de colonne, etidla fiche à mettre à jour. La fonction retourne{ created?, updated?, failures?: { rowIndex, message? }[] }. Lorsqu'elle est fournie, elle écrit toutes les lignes à la place decreateetupdate.Une erreur levée met en échec toutes les lignes de son lot avec son message, et l'import continue. Une erreur portant un
status401 ou 403, ou uncodeunauthorized,forbiddenouinvalid_credentials, arrête l'import avec « Vous n'êtes pas autorisé à importer dans ce tableau. »lookup({ columnId, keys })retourne les identifiants de fiche par valeur de clé,Record<string, string>, pour la colonne clé choisie à l'écran.keyssont les valeurs de clé du fichier, converties comme la colonne. Sans elle, la table charge toutes les fiches vialistavec une requête vide (100 par page), ou utilise les lignes qu'elle contient lorsqu'il n'y a pas delist, et lit l'identifiant de chaque fiche avec legetRowIdde la table, sinonid.contextest le contexte de la vue, comme pour les destinations sur mesure :viewId,query,columns,selectedRowIds,urletloadRows.
Les types (TableImportActions, ImportSource, ImportBatch, ImportBatchResult) sont exportés par import-flow.ts et import-model.ts : components/ui/yayaw-table/utils/ en React, components/ui/yayaw-table-vue/ en Vue. Ils ne dépendent d'aucun framework, votre code serveur peut donc les importer aussi.
Importer côté serveur
Avec importRows, les lignes sont écrites en masse sur votre serveur au lieu d'une requête par ligne. Vérifiez-y les autorisations et validez de nouveau les valeurs : le navigateur les a déjà converties, mais une requête peut être falsifiée. Cette server action Next.js écrit un lot de projets et signale les échecs ligne par ligne :
"use server";
import type {
ImportBatch,
ImportBatchResult,
ImportRowFailure,
} from "@/components/ui/yayaw-table/utils/import-model";
import {
canImportProjects,
findProjectIdsByColumn,
insertProjects,
requireUser,
updateProjects,
validateProject,
} from "@/server/app";
const KEY_COLUMNS = new Set(["id", "name"]);
export async function importProjects(
batch: ImportBatch
): Promise<ImportBatchResult | { error: "forbidden" }> {
const user = await requireUser();
if (!(await canImportProjects(user))) {
return { error: "forbidden" };
}
const failures: ImportRowFailure[] = [];
const valid = <T extends { rowIndex: number; values: Record<string, unknown> }>(
rows: T[],
mode: "create" | "update"
) =>
rows.filter((row) => {
const problem = validateProject(row.values, mode); // par ex. "Le prix doit être positif"
if (problem) {
failures.push({ rowIndex: row.rowIndex, message: problem });
}
return !problem;
});
const creates = valid(batch.creates, "create");
const updates = valid(batch.updates, "update");
// Une requête par lot ; `updateProjects` ne touche que les fiches de
// l'utilisateur et retourne les identifiants qu'il n'a pas pu mettre à jour.
await insertProjects(user, creates.map((row) => row.values));
const missing = new Set(
await updateProjects(user, updates.map(({ id, values }) => ({ id, values })))
);
for (const row of updates) {
if (missing.has(row.id)) {
failures.push({ rowIndex: row.rowIndex, message: "Fiche introuvable" });
}
}
return {
created: creates.length,
updated: updates.length - missing.size,
failures,
};
}
export async function findProjectIds(request: { columnId: string; keys: string[] }) {
const user = await requireUser();
if (!KEY_COLUMNS.has(request.columnId)) {
return {};
}
// { "Alpha launch": "prj_12", … } pour les fiches visibles par l'utilisateur.
return await findProjectIdsByColumn(user, request.columnId, request.keys);
}Les fonctions importées depuis @/server/app représentent votre propre code. Next.js ne transmet pas le code d'une erreur d'une server action au navigateur : l'action retourne donc { error }, et les actions de la table le transforment en une erreur que l'import reconnaît :
import type { TableImportActions } from "@/components/ui/yayaw-table/utils/import-flow";
import { findProjectIds, importProjects } from "./import-projects";
export const projectImport: TableImportActions = {
importRows: async (batch) => {
const result = await importProjects(batch);
if ("error" in result) {
// `forbidden` arrête l'import avec le message d'autorisation.
throw Object.assign(new Error("Import not allowed"), { code: result.error });
}
return result;
},
lookup: findProjectIds,
batchSize: 200,
};Ajoutez import: projectImport aux actions de la table, à côté de list, create et update. En Vue, importez les types depuis @/components/ui/yayaw-table-vue/import-flow et @/components/ui/yayaw-table-vue/import-model, exposez les mêmes fonctions comme routes serveur Nuxt et appelez-les avec $fetch.
Autres sources
sources ajoute des choix après le CSV, comme un CRM, une base de données Notion ou un onglet Google Sheets. Chacune est { id, label, description?, load(context) }, et load retourne un tableau de chaînes ou du texte CSV :
sources: [
{
id: "crm",
label: "Contacts du CRM",
description: "Contacts mis à jour cette semaine",
load: async () => {
const contacts = await loadCrmContacts(); // votre fonction serveur
return {
name: "Contacts du CRM",
headers: ["Nom", "E-mail", "Société"],
rows: contacts.map((contact) => [contact.name, contact.email, contact.company]),
};
},
},
],Une source qui retourne { name?, text } est lue comme du CSV collé. Les lignes passent ensuite par la même correspondance, la même vérification et le même import. Mettez csv: false pour ne proposer que vos sources. Une source qui lève une erreur affiche « La source n'a pas pu être lue. », ou le message d'autorisation pour une erreur de connexion ou d'autorisation.
Les destinations de connexion dont le connecteur sait importer sont listées aussi, sous la forme « Depuis Google Sheets » (« Importe ses enregistrements et les garde liés. »). Elles ouvrent l'écran du connecteur avec le sens positionné sur l'import, au lieu de cet écran : les enregistrements sont associés champ par champ, prévisualisés et importés par le sync du connecteur, et restent liés à la cible pour les synchronisations suivantes. Retour et Terminé reviennent à Importer. table.sync: false masque ces sources.
Libellés
Chaque libellé de l'écran a un texte anglais et français intégré et peut être remplacé, clé par clé, par les traductions import.<clé> : import.title renomme l'entrée du menu Données et l'écran, import.keyColumn la liste de la clé, import.skipErrors la case à cocher, etc. La page des traductions liste toutes les clés.
Mettez table.import: false dans la configuration pour masquer Importer tout en conservant create et update pour les formulaires de la table.