npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@applite/duticotac-react

v0.0.11

Published

React UI components for Duticotac payments (Shadcn-based)

Readme

@applite/duticotac-react

Composants React pour integrer les paiements Duticotac dans vos applications. Construit avec Tailwind CSS et compatible avec les tokens Shadcn UI.

Ce package fournit une experience de paiement complete : selection du provider, saisie du numero de telephone, code OTP, suivi en temps reel de la transaction, et affichage du resultat.


Table des matieres


Installation

pnpm add @applite/duticotac-react

C'est tout ! Le SDK core (@applite/duticotac) est inclus et re-exporte automatiquement. Vous n'avez pas besoin de l'installer separement.

Prerequis

  • React >= 18
  • Tailwind CSS configure dans votre projet
  • Les composants utilisent les tokens de couleur Shadcn (primary, muted-foreground, border, destructive, etc.). Si vous utilisez Shadcn UI, tout fonctionne directement. Sinon, definissez ces variables CSS dans votre theme Tailwind.

Demarrage rapide

La maniere recommandee d'utiliser ce package est le hook useDuticotac. Il fournit un dialog responsive integre (modal sur desktop, bottom sheet sur mobile) et une API imperative simple :

import { DuticotacSDK, useDuticotac } from "@applite/duticotac-react";

const sdk = new DuticotacSDK({ apiKey: "your_api_key" });

function CheckoutPage() {
  const { pay, Dialog } = useDuticotac({
    sdk,
    getTransactionId: async () => {
      const res = await fetch("/api/create-transaction", { method: "POST" });
      const data = await res.json();
      return data.transactionId;
    },
  });

  return (
    <>
      <button onClick={() => pay({
        amount: 5000,
        onSuccess: (tx) => console.log("Paiement confirme :", tx),
      })}>
        Acheter — 5 000 FCFA
      </button>
      {Dialog}
    </>
  );
}

Tous les types et classes du SDK core (DuticotacSDK, DuticotacError, types, etc.) sont disponibles directement depuis @applite/duticotac-react. Pas besoin d'importer depuis @applite/duticotac.

Pour un controle plus fin (modal custom, Shadcn Dialog, etc.), utilisez directement le composant DuticotacPaymentModal avec sa prop renderModal.


Hook useDuticotac

La maniere la plus simple d'integrer Duticotac. Le hook retourne une fonction pay() et un element Dialog avec un dialog responsive integre (modal centre sur desktop, bottom sheet sur mobile).

import { DuticotacSDK, useDuticotac } from "@applite/duticotac-react";

const sdk = new DuticotacSDK({ apiKey: "your_api_key" });

function CheckoutPage() {
  const { pay, Dialog } = useDuticotac({
    sdk,
    providers: ["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"],
    getTransactionId: async () => {
      const res = await fetch("/api/create-transaction", { method: "POST" });
      const data = await res.json();
      return data.transactionId;
    },
    name: "Jean Dupont",
    email: "[email protected]",
  });

  return (
    <>
      <button onClick={() => pay({
        amount: 5000,
        productReference: "premium-monthly",
        onSuccess: (tx) => {
          console.log("Paiement confirme :", tx);
        },
      })}>
        Acheter — 5 000 FCFA
      </button>
      {Dialog}
    </>
  );
}

API du hook

const { pay, Dialog, isOpen, close } = useDuticotac(config);

config (UseDuticotacConfig)

| Champ | Type | Requis | Description | |-------|------|--------|-------------| | sdk | DuticotacSDK | Oui | Instance du SDK | | getTransactionId | () => Promise<string> | Oui | Generateur d'ID transaction | | providers | PaymentProvider[] | Non | Providers disponibles (defaut: OM, MTN, MOOV, WAVE) | | productReference | string | Non | Reference produit par defaut | | name | string | Non | Nom du client par defaut | | email | string | Non | Email du client par defaut | | customerId | string | Non | ID du client | | kolaboReference | string | Non | Reference Kolabo | | app | CoreApp | Non | Application source | | platform | PlatformType | Non | Plateforme | | initialPhone | string | Non | Pre-remplir le telephone | | pollTimeout | number | Non | Timeout polling en ms (defaut: 90000) | | title | string | Non | Titre du dialog (defaut: "Paiement") |

Retour

| Champ | Type | Description | |-------|------|-------------| | pay | (options: PayOptions) => void | Ouvre le dialog de paiement | | Dialog | ReactNode | Element JSX a rendre une fois dans l'arbre | | isOpen | boolean | Etat du dialog | | close | () => void | Fermer le dialog programmatiquement |

PayOptions (argument de pay())

| Champ | Type | Requis | Description | |-------|------|--------|-------------| | amount | number | Oui | Montant en FCFA | | providers | PaymentProvider[] | Non | Override les providers | | productReference | string | Non | Override la reference produit | | name | string | Non | Override le nom | | email | string | Non | Override l'email | | customerId | string | Non | Override l'ID client | | successMessage | string | Non | Message de succes personnalise | | onSuccess | (tx: TransactionModel) => void | Non | Callback de succes | | onClose | () => void | Non | Callback de fermeture |

Comportement responsive

Le dialog s'adapte automatiquement :

| Ecran | Comportement | |-------|-------------| | Desktop (> 640px) | Modal centre, max-width 440px, animation scale | | Mobile (<= 640px) | Bottom sheet, glisse depuis le bas, coins arrondis en haut, poignee de drag |

Les deux modes incluent : fermeture par backdrop click, touche Escape, verrouillage du scroll, et transitions CSS fluides.


Flux de paiement

Le DuticotacPaymentModal gere automatiquement les 4 etapes du paiement :

1. FORMULAIRE          2. CONFIRMATION         3. SUCCES / ERREUR
┌──────────────┐      ┌──────────────┐        ┌──────────────┐
│ [OM] [MTN]   │      │   Spinner    │        │     [OK]     │
│ [MOOV] [WAVE]│ ───> │              │ ────>  │   Paiement   │
│              │      │ Confirmez    │        │   reussi !   │
│ Tel: 07...   │      │ sur votre    │        │              │
│ OTP: ____    │      │ telephone... │        │  [Fermer]    │
│              │      │              │        │              │
│ [Payer 5000] │      │ 42s...       │        │  — ou —      │
└──────────────┘      └──────────────┘        │              │
                                              │     [X]      │
                                              │   Echec du   │
                                              │   paiement   │
                                              │ [Reessayer]  │
                                              └──────────────┘

Etape 1 — Formulaire : L'utilisateur choisit un provider, entre son numero de telephone, et son code OTP (Orange Money uniquement).

Etape 2 — Confirmation : Le SDK appelle mobileMoney.cashout() puis transaction.poll(). Si le provider retourne une paymentUrl (Wave), un nouvel onglet s'ouvre. Le polling est intelligent : il pause quand l'onglet est cache et reprend instantanement quand l'utilisateur revient.

Etape 3 — Resultat : Succes (avec callback onSuccess) ou erreur (avec bouton "Reessayer").


Composants

DuticotacPaymentModal

Le composant principal qui orchestre tout le flux de paiement.

import { DuticotacPaymentModal } from "@applite/duticotac-react";

Props

| Prop | Type | Requis | Defaut | Description | |------|------|--------|--------|-------------| | open | boolean | Oui | — | Controle la visibilite du modal | | onClose | () => void | Oui | — | Appele quand le modal se ferme | | sdk | DuticotacSDK | Oui | — | Instance du SDK Duticotac | | amount | number | Oui | — | Montant du paiement en FCFA | | getTransactionId | () => Promise<string> | Oui | — | Fonction async retournant un ID de transaction unique | | providers | PaymentProvider[] | Non | ["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"] | Providers de paiement disponibles | | productReference | string | Non | — | Reference du produit/offre | | onSuccess | (tx: TransactionModel) => void | Non | — | Appele quand le paiement est confirme | | initialPhone | string | Non | "" | Pre-remplir le numero de telephone | | name | string | Non | "Duticotac App" | Nom du client | | email | string | Non | "" | Email du client | | customerId | string | Non | — | ID du client | | kolaboReference | string | Non | — | Reference Kolabo partenaire | | app | CoreApp | Non | — | Application source ("XORAIA", "MONSMSPRO", "FREE") | | platform | PlatformType | Non | — | Plateforme ("STORE", "DUTICOTAC", etc.) | | title | string | Non | "Paiement" | Titre du modal | | successMessage | string | Non | "Le paiement a ete effectue avec succes." | Message affiche apres succes | | pollTimeout | number | Non | 90000 | Timeout du polling en ms | | className | string | Non | — | Classes CSS additionnelles | | renderModal | (props) => ReactNode | Non | — | Render prop pour utiliser votre propre modal |


ResponsiveDialog

Dialog responsive qui s'adapte automatiquement : modal centre sur desktop, bottom sheet sur mobile. Utilise en interne par useDuticotac, mais peut etre utilise independamment.

import { ResponsiveDialog } from "@applite/duticotac-react";

<ResponsiveDialog
  open={isOpen}
  onClose={() => setOpen(false)}
  title="Mon dialog"
>
  <p>Contenu du dialog</p>
</ResponsiveDialog>

Props

| Prop | Type | Requis | Description | |------|------|--------|-------------| | open | boolean | Oui | Visibilite du dialog | | onClose | () => void | Oui | Callback de fermeture | | title | string | Oui | Titre affiche dans le header | | children | ReactNode | Oui | Contenu du dialog | | className | string | Non | Classes CSS additionnelles |

Fonctionnalites

  • Desktop (> 640px) : Modal centre, max-width 440px, animation d'echelle
  • Mobile (<= 640px) : Bottom sheet, slide-up, coins arrondis en haut, poignee de drag
  • Rendu via createPortal dans document.body
  • Fermeture : backdrop click, touche Escape
  • Verrouillage du scroll body quand ouvert
  • Transitions CSS fluides (250ms)

ProviderSelector

Grille de selection des providers de paiement avec logos et indicateur de selection.

import { ProviderSelector } from "@applite/duticotac-react";

function MyForm() {
  const [provider, setProvider] = useState<PaymentProvider>("OM_CI");

  return (
    <ProviderSelector
      providers={["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"]}
      value={provider}
      onChange={setProvider}
      disabled={isLoading}
      className="my-4"
    />
  );
}

Props

| Prop | Type | Requis | Description | |------|------|--------|-------------| | providers | PaymentProvider[] | Oui | Liste des providers a afficher | | value | PaymentProvider | Oui | Provider actuellement selectionne | | onChange | (provider: PaymentProvider) => void | Oui | Callback de changement | | disabled | boolean | Non | Desactiver la selection | | className | string | Non | Classes CSS additionnelles |


PhoneInput

Champ de saisie de numero de telephone avec prefixe +225 (Cote d'Ivoire).

import { PhoneInput } from "@applite/duticotac-react";

<PhoneInput
  value={phone}
  onChange={setPhone}
  label="Numero de telephone"
  placeholder="07 01 02 03 04"
  error={phoneError}
  disabled={isLoading}
  autoFocus
/>

Props

| Prop | Type | Requis | Defaut | Description | |------|------|--------|--------|-------------| | value | string | Oui | — | Valeur du champ (sans prefixe +225) | | onChange | (value: string) => void | Oui | — | Callback de changement | | label | string | Non | "Numero de telephone" | Label du champ | | placeholder | string | Non | "07 01 02 03 04" | Placeholder | | error | string \| null | Non | — | Message d'erreur a afficher | | disabled | boolean | Non | — | Desactiver le champ | | autoFocus | boolean | Non | — | Focus automatique | | className | string | Non | — | Classes CSS additionnelles |


OtpInput

Saisie de code OTP a 4 chiffres. Utilise pour Orange Money (OM_CI). Gere automatiquement le focus entre les champs, le collage, et la navigation au clavier.

import { OtpInput } from "@applite/duticotac-react";

<OtpInput
  value={otp}
  onChange={setOtp}
  length={4}
  disabled={isLoading}
/>

Props

| Prop | Type | Requis | Defaut | Description | |------|------|--------|--------|-------------| | value | string | Oui | — | Code OTP saisi | | onChange | (value: string) => void | Oui | — | Callback de changement | | length | number | Non | 4 | Nombre de chiffres | | disabled | boolean | Non | — | Desactiver la saisie | | className | string | Non | — | Classes CSS additionnelles |

Fonctionnalites :

  • Auto-focus vers le champ suivant apres saisie d'un chiffre
  • Retour au champ precedent sur Backspace
  • Navigation avec les fleches gauche/droite
  • Support du copier-coller (colle automatiquement les 4 chiffres)
  • Instruction integree : "Faites #144*82# pour recevoir le code"

TransactionStatus

Affiche l'etat de la transaction : spinner de polling, succes, ou erreur. Utilise en interne par DuticotacPaymentModal, mais peut etre utilise independamment.

import { TransactionStatus } from "@applite/duticotac-react";

// Polling en cours
<TransactionStatus
  status="polling"
  elapsedSeconds={42}
  paymentUrl="https://wave.com/pay/..."
  onOpenPaymentUrl={() => window.open(url, "_blank")}
  onClose={handleClose}
/>

// Succes
<TransactionStatus
  status="success"
  message="500 credits ajoutes a votre compte."
  onClose={handleClose}
/>

// Erreur
<TransactionStatus
  status="error"
  errorMessage="Le delai de confirmation a expire."
  onRetry={handleRetry}
  onClose={handleClose}
/>

Props

| Prop | Type | Requis | Description | |------|------|--------|-------------| | status | "polling" \| "success" \| "error" | Oui | Etat actuel | | elapsedSeconds | number | Non | Secondes ecoulees (affiche pendant le polling) | | message | string | Non | Message de succes | | errorMessage | string | Non | Message d'erreur | | paymentUrl | string \| null | Non | URL de paiement externe (Wave) | | onRetry | () => void | Non | Callback du bouton "Reessayer" | | onClose | () => void | Non | Callback du bouton "Fermer" | | onOpenPaymentUrl | () => void | Non | Callback pour ouvrir l'URL de paiement | | className | string | Non | Classes CSS additionnelles |


Utilitaires

Validation du telephone

import { validatePhone, normalizePhone, getPhoneRegex, needsOtp } from "@applite/duticotac-react";

// Valider un numero pour un provider specifique
const error = validatePhone("0701020304", "OM_CI");
// null si valide, message d'erreur sinon

// Normaliser en format international
normalizePhone("0701020304"); // "+2250701020304"

// Obtenir la regex de validation
getPhoneRegex("OM_CI");  // /^(\+225)(07)[0-9]{8}$/
getPhoneRegex("MTN_CI"); // /^(\+225)(05)[0-9]{8}$/
getPhoneRegex("MOOV_CI");// /^(\+225)(01)[0-9]{8}$/

// Verifier si l'OTP est requis
needsOtp("OM_CI");   // true
needsOtp("MTN_CI");  // false
needsOtp("WAVE_CI"); // false

Formatage

import { formatCFA } from "@applite/duticotac-react";

formatCFA(5000);    // "5 000"
formatCFA(1500000); // "1 500 000"

Classes CSS

import { cn } from "@applite/duticotac-react";

cn("px-4 py-2", isActive && "bg-primary", className);
// Merge intelligent des classes Tailwind (via clsx + tailwind-merge)

Integration avec Shadcn UI

Avec Shadcn Dialog

import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { DuticotacPaymentModal } from "@applite/duticotac-react";

<DuticotacPaymentModal
  open={open}
  onClose={() => setOpen(false)}
  sdk={sdk}
  amount={5000}
  getTransactionId={getTransactionId}
  renderModal={({ open, onClose, title, children }) => (
    <Dialog open={open} onOpenChange={(v) => { if (!v) onClose(); }}>
      <DialogContent className="sm:max-w-md">
        <DialogHeader>
          <DialogTitle>{title}</DialogTitle>
        </DialogHeader>
        {children}
      </DialogContent>
    </Dialog>
  )}
/>

Avec Shadcn Drawer (mobile)

import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from "@/components/ui/drawer";

<DuticotacPaymentModal
  open={open}
  onClose={() => setOpen(false)}
  sdk={sdk}
  amount={5000}
  getTransactionId={getTransactionId}
  renderModal={({ open, onClose, title, children }) => (
    <Drawer open={open} onOpenChange={(v) => { if (!v) onClose(); }}>
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>{title}</DrawerTitle>
        </DrawerHeader>
        <div className="px-4 pb-6">{children}</div>
      </DrawerContent>
    </Drawer>
  )}
/>

Sans Shadcn (modal integre)

Si vous ne passez pas de renderModal, un modal overlay simple est utilise automatiquement. Aucune dependance externe requise.


Providers supportes

| Code | Nom | OTP requis | Prefixe telephone | Particularite | |------|-----|------------|-------------------|---------------| | OM_CI | Orange Money | Oui (4 chiffres) | 07 | L'utilisateur fait #144*82# pour obtenir le code OTP | | MTN_CI | MTN MoMo | Non | 05 | Confirmation sur le telephone | | MOOV_CI | Moov (Flooz) | Non | 01 | Confirmation sur le telephone | | WAVE_CI | Wave | Non | 07, 05, 01 | Ouvre un onglet navigateur pour le paiement | | CREDIT_CARD | Carte de credit | Non | — | A venir | | CASH | Especes | Non | — | Paiement en main propre | | IAP | Achat Integre | Non | — | In-App Purchase (mobile) |


Validation du telephone

Chaque provider a des regles de validation specifiques basees sur les prefixes telephoniques de Cote d'Ivoire :

| Provider | Regex | Exemples valides | |----------|-------|------------------| | Orange Money (OM_CI) | +225 07 XX XX XX XX | +225 07 01 02 03 04 | | MTN (MTN_CI) | +225 05 XX XX XX XX | +225 05 01 02 03 04 | | Moov (MOOV_CI) | +225 01 XX XX XX XX | +225 01 01 02 03 04 | | Wave (WAVE_CI) | +225 (07\|05\|01) XX XX XX XX | Accepte tous les prefixes |


Gestion des erreurs

Le modal gere automatiquement les erreurs du SDK et affiche des messages en francais :

| Code erreur | Message affiche | |-------------|-----------------| | otp-required | Code OTP requis pour les paiements Orange Money | | ref-or-idFromClient-required | Reference ou IDFromClient requis | | payment-failed | Echec du paiement | | payment-cancelled | Paiement annule | | polling-timeout | Temps d'attente de la transaction expire | | phone-required | Numero de telephone requis | | no-api-key | Cle API non fournie | | app-not-found | Application non trouvee dans AppLite UI | | unknown-error | Une erreur inconnue est survenue |

En cas d'erreur, l'utilisateur peut cliquer sur "Reessayer" pour revenir au formulaire.


Comportement du polling

Apres l'appel cashout, le composant suit le statut de la transaction via sdk.transaction.poll() :

  • Backoff exponentiel : 1s → 2s → 3s → 5s → 8s (meme strategie que le SDK Dart)
  • Timeout : 90 secondes par defaut (configurable via pollTimeout)
  • Tab-aware : Le polling pause quand l'onglet est cache (utilisateur sur Wave ou sur son telephone) et reprend immediatement quand l'onglet redevient visible
  • Compteur : Un indicateur "Verification en cours... 42s" est affiche
  • Wave : Si le provider retourne une paymentUrl, un nouvel onglet s'ouvre automatiquement avec un bouton de fallback si le popup est bloque
  • Etats terminaux :
    • CONFIRMED → ecran de succes + callback onSuccess
    • CANCELLED → ecran d'erreur ("Paiement annule")
    • FAILED → ecran d'erreur ("Echec du paiement")

API Reference complete

Tout est disponible depuis un seul import @applite/duticotac-react :

import {
  // SDK core (re-exporte depuis @applite/duticotac)
  DuticotacSDK,
  DuticotacError,
  getProviderName,
  getProviderLogo,
  getErrorMessage,

  // Hook (recommande)
  useDuticotac,

  // Composants
  DuticotacPaymentModal,
  ResponsiveDialog,
  ProviderSelector,
  PhoneInput,
  OtpInput,
  TransactionStatus,

  // Utilitaires
  cn,
  formatCFA,
  validatePhone,
  normalizePhone,
  getPhoneRegex,
  needsOtp,
} from "@applite/duticotac-react";

import type {
  // Types SDK
  PaymentProvider,    // "OM_CI" | "MTN_CI" | "MOOV_CI" | "WAVE_CI" | "CREDIT_CARD" | "CASH" | "IAP"
  PlatformType,       // "STORE" | "TRANSPORT" | "RESTAURATION" | "MULTI_SERVICE" | "E_LEARNING" | "DUTICOTAC"
  CoreApp,            // "XORAIA" | "MONSMSPRO" | "FREE"
  TransactionModel,
  TransactionStatus as TransactionStatusType,

  // Types des props
  UseDuticotacConfig,
  UseDuticotacReturn,
  PayOptions,
  DuticotacPaymentModalProps,
  ResponsiveDialogProps,
  ProviderSelectorProps,
  PhoneInputProps,
  OtpInputProps,
  TransactionStatusProps,
} from "@applite/duticotac-react";

Exemples avances

Avec un backend personnalise

function PaymentPage({ offer, apiKey }: { offer: Offer; apiKey: string }) {
  const [open, setOpen] = useState(false);
  const sdk = useMemo(() => new DuticotacSDK({ apiKey }), [apiKey]);

  const getTransactionId = async () => {
    const res = await fetch("/api/payments/init", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ offerId: offer.id }),
    });
    const { transactionId } = await res.json();
    return transactionId;
  };

  return (
    <DuticotacPaymentModal
      open={open}
      onClose={() => setOpen(false)}
      sdk={sdk}
      amount={offer.price}
      getTransactionId={getTransactionId}
      productReference={offer.ref}
      providers={["OM_CI", "MTN_CI", "WAVE_CI"]}
      pollTimeout={120_000}
      onSuccess={(tx) => {
        toast.success(`Paiement confirme ! Ref: ${tx.ref}`);
        router.push("/dashboard");
      }}
    />
  );
}

Composants individuels (sans le modal)

Vous pouvez utiliser les composants independamment pour construire votre propre flux :

import {
  ProviderSelector,
  PhoneInput,
  OtpInput,
  TransactionStatus,
  needsOtp,
  validatePhone,
} from "@applite/duticotac-react";

function CustomPaymentForm() {
  const [provider, setProvider] = useState<PaymentProvider>("OM_CI");
  const [phone, setPhone] = useState("");
  const [otp, setOtp] = useState("");

  return (
    <form>
      <ProviderSelector
        providers={["OM_CI", "MTN_CI", "WAVE_CI"]}
        value={provider}
        onChange={(p) => {
          setProvider(p);
          if (!needsOtp(p)) setOtp("");
        }}
      />

      <PhoneInput
        value={phone}
        onChange={setPhone}
        error={validatePhone(phone, provider)}
      />

      {needsOtp(provider) && (
        <OtpInput value={otp} onChange={setOtp} />
      )}

      <button type="submit">Payer</button>
    </form>
  );
}

Personnalisation du style

Tous les composants acceptent une prop className pour ajouter des classes Tailwind :

<ProviderSelector
  providers={providers}
  value={selected}
  onChange={setSelected}
  className="gap-4 grid-cols-2" // Override la grille
/>

<PhoneInput
  value={phone}
  onChange={setPhone}
  className="mb-6" // Espacement custom
/>

Scripts

| Commande | Description | |----------|-------------| | pnpm build | Build CJS + ESM avec declarations de types | | pnpm dev | Mode watch pour le developpement | | pnpm type-check | Verification des types TypeScript | | pnpm lint | Lint du code source |