feat: implement DLC management page with EAN lookup, filtering, and CRUD operations

This commit is contained in:
Michael committed 2026-04-02 09:20:54 +02:00
1 parent fcf8518684
commit b090c8f36a
2 files changed
+114 -10

No files matched your search

+58 -7
View File
@@ -1,4 +1,4 @@
import { useState, useEffect, useMemo } from "react";
import { useState, useEffect, useMemo, useRef } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
@@ -12,7 +12,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Textarea } from "@/components/ui/textarea";
import { AlertTriangle, Plus, Eye, Edit, Trash2, CheckCircle, CheckCircle2, Package, Clock, AlertCircle, Filter, Download, FileText, PackageX, RotateCcw, X } from "lucide-react";
import { AlertTriangle, Plus, Eye, Edit, Trash2, CheckCircle, CheckCircle2, Package, Clock, AlertCircle, Filter, Download, FileText, PackageX, RotateCcw, X, Loader2 } from "lucide-react";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -115,6 +115,51 @@ export default function DlcPage() {
},
});
// EAN lookup from external API
const [eanLookupLoading, setEanLookupLoading] = useState(false);
const eanDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const gencodeValue = form.watch("gencode");
useEffect(() => {
if (!gencodeValue || gencodeValue.length < 8) return;
if (eanDebounceRef.current) clearTimeout(eanDebounceRef.current);
eanDebounceRef.current = setTimeout(async () => {
setEanLookupLoading(true);
try {
const res = await fetch(`https://api.ffnancy.fr/api/articles?ean=${encodeURIComponent(gencodeValue)}&limit=1`);
if (!res.ok) return;
const data = await res.json();
const article = data.articles?.[0];
if (!article) return;
// Toujours mettre à jour le nom du produit
form.setValue("productName", article.libelle1, { shouldValidate: true });
// Matcher le fournisseur par nom
if (article.nom_fou_principal) {
const apiName = article.nom_fou_principal.toLowerCase().trim();
const matched = (suppliers as any[]).find((s: any) =>
s.name.toLowerCase().trim().includes(apiName) ||
apiName.includes(s.name.toLowerCase().trim())
);
if (matched) {
form.setValue("supplierId", matched.id, { shouldValidate: true });
}
}
} catch {
// Lookup best-effort, on ignore les erreurs
} finally {
setEanLookupLoading(false);
}
}, 600);
return () => {
if (eanDebounceRef.current) clearTimeout(eanDebounceRef.current);
};
}, [gencodeValue, suppliers]);
// Create mutation - optimized cache invalidation
const createMutation = useMutation({
mutationFn: (data: InsertDlcProduct) => apiRequest("/api/dlc-products", "POST", data),
@@ -670,11 +715,17 @@ export default function DlcPage() {
<FormItem>
<FormLabel>Code EAN13 (optionnel)</FormLabel>
<FormControl>
<Input
{...field}
value={field.value || ""}
placeholder="1234567890123"
/>
<div className="relative">
<Input
{...field}
value={field.value || ""}
placeholder="1234567890123"
className={eanLookupLoading ? "pr-8" : ""}
/>
{eanLookupLoading && (
<Loader2 className="absolute right-2 top-1/2 -translate-y-1/2 w-4 h-4 animate-spin text-blue-500" />
)}
</div>
</FormControl>
<FormMessage />
</FormItem>