mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
feat: implement DLC management page with EAN lookup, filtering, and CRUD operations
This commit is contained in:
1 parent
fcf8518684
commit
b090c8f36a
2 files changed
+114
-10
No files matched your search
@@ -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>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* MobileDlcPage.tsx
|
||||
* Version mobile de la page Gestion des DLC
|
||||
*/
|
||||
import { useState, useMemo } from "react";
|
||||
import { useState, useMemo, useEffect, useRef } from "react";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
import { useStore } from "@/contexts/StoreContext";
|
||||
@@ -20,7 +20,8 @@ import {
|
||||
CheckCircle,
|
||||
X,
|
||||
Calendar,
|
||||
MoreVertical
|
||||
MoreVertical,
|
||||
Loader2
|
||||
} from "lucide-react";
|
||||
import { format, parseISO } from "date-fns";
|
||||
import { fr } from "date-fns/locale";
|
||||
@@ -139,6 +140,49 @@ export default function MobileDlcPage() {
|
||||
}
|
||||
});
|
||||
|
||||
// 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;
|
||||
|
||||
form.setValue("productName", article.libelle1, { shouldValidate: true });
|
||||
|
||||
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]);
|
||||
|
||||
const onSubmit = (data: any) => {
|
||||
let groupId = selectedStoreId;
|
||||
if (!groupId && user?.userGroups?.[0]?.groupId) groupId = user.userGroups[0].groupId;
|
||||
@@ -386,7 +430,16 @@ export default function MobileDlcPage() {
|
||||
<FormItem>
|
||||
<FormLabel>Gencode (Scanner si dispo)</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="EAN13" {...field} />
|
||||
<div className="relative">
|
||||
<Input
|
||||
placeholder="EAN13"
|
||||
{...field}
|
||||
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>
|
||||
</FormItem>
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user