"use client"; /** * CollectFlow — Informations de connexion à l'API `/api/v1`. * * Tout ce qu'il faut pour appeler l'API depuis un script : URL de base, en-tête * d'authentification, liste des endpoints et exemples copiables. L'URL de base est * déduite de l'origine courante, pour rester juste quel que soit le déploiement. */ import { useEffect, useState, type ReactNode } from "react"; import { Copy, Check, ExternalLink, Bot, ChevronRight } from "lucide-react"; import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { toast } from "@/components/ui/feedback"; function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false); const copier = async () => { try { await navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 1500); } catch { toast.erreur("La copie automatique a échoué : sélectionnez le texte et copiez-le à la main."); } }; return ( ); } function CodeLine({ children, copy }: { children: string; copy?: string }) { return (
{children}
); } function Bloc({ titre, children }: { titre: ReactNode; children: ReactNode }) { return (

{titre}

{children}
); } /** Référence technique repliée : utile aux développeurs, superflue pour les autres. */ function Repli({ titre, children }: { titre: ReactNode; children: ReactNode }) { return (
{titre}
{children}
); } const ENDPOINTS: Array<{ method: string; path: string; desc: string }> = [ { method: "GET", path: "/fournisseurs", desc: "Fournisseurs, avec la date de calcul de leur grille" }, { method: "GET", path: "/grid?fournisseur=CODE", desc: "Lignes de la grille d'un fournisseur" }, { method: "GET", path: "/nomenclatures?fournisseur=CODE", desc: "Postes de nomenclature d'un fournisseur, avec nombre d'articles et chiffre d'affaires" }, { method: "GET", path: "/products/search?q=terme", desc: "Recherche de produits, tous fournisseurs confondus" }, { method: "GET", path: "/products/{codein}", desc: "Fiche complète d'un produit" }, { method: "PUT", path: "/products/{codein}/gamme", desc: "Affecter ou changer la gamme d'un produit — corps : { \"gamme\": \"A\" }" }, { method: "POST", path: "/gammes", desc: "Affecter ou changer la gamme de plusieurs produits d'un fournisseur en un appel" }, { method: "GET", path: "/network/{codeCentrale}", desc: "Ventes du réseau (Qlik) et courbe sur 12 mois" }, { method: "GET", path: "/openapi.json", desc: "Description de l'API lisible par un programme (format OpenAPI)" }, ]; const PARAMS: Array<{ name: string; desc: string }> = [ { name: "page, limit", desc: "Pagination, sans plafond. Sur /grid, omettre limit renvoie TOUT le fournisseur en un appel ; ailleurs le défaut est 100." }, { name: "sort, order", desc: "Tri, ex. sort=totalCa&order=desc." }, { name: "search", desc: "Libellé, codein, GTIN, référence ou code centrale." }, { name: "nomenclature", desc: "Début du code de nomenclature : 32 (univers), 3202 (famille), 320211 (sous-famille)." }, { name: "gamme, code1..code3", desc: "Filtres sur la gamme et les codes exacts de nomenclature." }, { name: "fields", desc: "Champs à conserver, séparés par des virgules : allège fortement la réponse." }, { name: "enrich", desc: "1 par défaut : ventes du réseau (Qlik) et gamme enregistrée relues à chaque appel. 0 pour s'en dispenser." }, { name: "compute", desc: "1 par défaut : calcule le fournisseur s'il n'a jamais été ouvert (premier appel plus lent). 0 pour échouer tout de suite." }, ]; export function ApiConnectionInfo() { const [origin, setOrigin] = useState(""); // `window` n'existe pas au rendu serveur : lire l'origine après montage est le // seul moyen d'afficher l'URL réelle sans provoquer d'écart d'hydratation. // eslint-disable-next-line react-hooks/set-state-in-effect useEffect(() => { setOrigin(window.location.origin); }, []); const base = `${origin || "https://votre-domaine"}/api/v1`; return ( {base}

Chaque appel doit porter une clé (à créer dans le cadre « Clés d'accès à l'API » ci-dessous), dans l'un ou l'autre de ces en-têtes. Depuis un navigateur déjà connecté à CollectFlow, aucune clé n'est nécessaire.

X-API-Key: VOTRE_CLE Authorization: Bearer VOTRE_CLE
{/* Branchement d'une IA externe (ChatGPT) */} Connecter une IA externe (ChatGPT) } >
  1. Créez une clé dans le cadre « Clés d'accès à l'API » ci-dessous et copiez-la.
  2. Dans ChatGPT : Créer un GPT → onglet Configurer → Créer une action.
  3. Cliquez sur Importer depuis une URL et collez l'adresse de la description de l'API :
    {`${base}/openapi.json`}
    Cette description est publique (elle ne contient aucune donnée) pour que ChatGPT puisse la lire. L'application doit être joignable depuis Internet.
  4. Dans Authentification, choisissez Clé d'API, type{" "} Personnalisé, nom d'en-tête X-API-Key, et collez votre clé.
  5. Testez avec une question du type « cherche les produits tapis » : le GPT appellera rechercherProduits.

Si l'adresse publique de l'application diffère de celle affichée ici, renseignez la variable d'environnement COLLECTFLOW_PUBLIC_URL : elle fixe l'adresse annoncée dans la description.

{ENDPOINTS.map((e) => ( ))}
Méthode Adresse Contenu
{e.method} {e.path} {e.desc}
{PARAMS.map((p) => ( ))}
Paramètre Effet
{p.name} {p.desc}
{`curl -X PUT -H "X-API-Key: VOTRE_CLE" -H "Content-Type: application/json" \\\n -d '{"gamme":"B"}' "${base}/products/123456/gamme"`} {`curl -H "X-API-Key: VOTRE_CLE" \\\n "${base}/products/search?q=tapis&limit=20"`} {`curl -H "X-API-Key: VOTRE_CLE" \\\n "${base}/grid?fournisseur=FOU001&fields=codein,libelle1,totalCa,codeGammeServeur"`}

Tout un fournisseur en un seul appel : il suffit d'omettre limit, et meta.complet confirme qu'il ne reste rien à lire.

{`curl -H "X-API-Key: VOTRE_CLE" \\\n "${base}/grid?fournisseur=FOU001"`}

Gros fournisseur (plusieurs dizaines de milliers d'articles) : lister d'abord les postes de nomenclature, puis les traiter un par un.

{`curl -H "X-API-Key: VOTRE_CLE" \\\n "${base}/nomenclatures?fournisseur=D005&niveau=1"`} {`curl -H "X-API-Key: VOTRE_CLE" \\\n "${base}/grid?fournisseur=D005&nomenclature=32"`}
{/* Comportement à connaître */}

Bon à savoir

L'API lit des grilles déjà calculées, enregistrées quand la Grille est ouverte dans l'application ou par le préchauffage ci-dessous. Un fournisseur jamais calculé l'est au premier appel, qui est alors plus lent ; avec compute=0, l'API répond aussitôt 202 not_ready.

Les ventes du réseau (network) et la gamme enregistrée (codeGammeServeur) sont relues à chaque appel.{" "} network vaut null quand le produit n'a pas de données réseau.

En cas d'erreur, la réponse a la forme {`{ "error": { "code", "message" } }`} avec le code 401 (clé absente, invalide ou révoquée), 400{" "} (paramètre incorrect) ou 404 (introuvable).

); }