"fix-guest-auto-flush-on-submit"

This commit is contained in:
deploy
2026-04-29 15:31:38 +02:00
parent 7e49de3d59
commit 518a7462f1
2 changed files with 44 additions and 6 deletions
+29 -3
View File
@@ -2,18 +2,42 @@
* Gestion de la liste d'invités pour une facture.
* Ajout / suppression d'invités (nom + entreprise).
*/
import { useState } from 'react';
import { useState, useImperativeHandle, forwardRef } from 'react';
import type { Guest } from '../types';
export interface GuestManagerHandle {
/**
* Si un invité est en cours de saisie (champ non validé), l'ajoute à la liste
* et retourne la liste complète (incluant ce nouvel invité).
* Retourne null si rien à flusher.
*/
flushPending: () => Guest[] | null;
}
interface Props {
guests: Guest[];
onChange: (guests: Guest[]) => void;
}
export default function GuestManager({ guests, onChange }: Props) {
const GuestManager = forwardRef<GuestManagerHandle, Props>(function GuestManager({ guests, onChange }, ref) {
const [name, setName] = useState('');
const [company, setCompany] = useState('');
useImperativeHandle(ref, () => ({
flushPending() {
const trimmed = name.trim();
if (!trimmed) return null;
const updated: Guest[] = [
...guests,
{ name: trimmed, company: company.trim() || null, sort_order: guests.length },
];
onChange(updated);
setName('');
setCompany('');
return updated;
},
}));
function addGuest() {
const trimmed = name.trim();
if (!trimmed) return;
@@ -94,4 +118,6 @@ export default function GuestManager({ guests, onChange }: Props) {
</div>
</div>
);
}
});
export default GuestManager;
+15 -3
View File
@@ -7,7 +7,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import toast from 'react-hot-toast';
import api from '../api/client';
import Camera from '../components/Camera';
import GuestManager from '../components/GuestManager';
import GuestManager, { type GuestManagerHandle } from '../components/GuestManager';
import { useOCR } from '../hooks/useOCR';
import { useOfflineQueue } from '../hooks/useOfflineQueue';
import type { Company, Category, Guest, InvoiceImage } from '../types';
@@ -34,6 +34,9 @@ export default function NewInvoice() {
// Ref pour retenir l'invoice_id créé si le send échoue hors ligne
const createdInvoiceIdRef = useRef<string | null>(null);
// Ref vers GuestManager pour flusher l'invité en cours de saisie
const guestManagerRef = useRef<GuestManagerHandle>(null);
// ── Étape du flux ──────────────────────────────────────────
const [step, setStep] = useState<Step>('capture');
@@ -101,6 +104,15 @@ export default function NewInvoice() {
createdInvoiceIdRef.current = null;
// Auto-ajouter l'invité en cours de saisie s'il n'a pas encore été
// validé via "Ajouter l'invité" (cas fréquent : l'utilisateur tape
// le nom et clique directement sur "Envoyer").
// flushPending() retourne la liste complète si un guest a été flushé,
// null sinon — on utilise cette valeur pour éviter un souci de timing
// avec la mise à jour asynchrone du state React.
const guestsFlushed = guestManagerRef.current?.flushPending() ?? null;
const finalGuests = guestsFlushed ?? guests;
const invoiceImages: InvoiceImage[] = images.map((img) => ({
path: img.filename,
order: img.order,
@@ -116,7 +128,7 @@ export default function NewInvoice() {
comment: comment || undefined,
images: invoiceImages,
add_to_tracking: addTracking,
guests,
guests: finalGuests,
});
// Mémoriser l'id en cas d'échec du send hors ligne
@@ -441,7 +453,7 @@ export default function NewInvoice() {
{showGuests && (
<div className="mt-3">
<GuestManager guests={guests} onChange={setGuests} />
<GuestManager ref={guestManagerRef} guests={guests} onChange={setGuests} />
</div>
)}
</div>