mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-19 05:07:57 +02:00
4 files changed
+58
-15
No files matched your search
@@ -16,7 +16,7 @@ localPort = 5000
|
|||||||
externalPort = 80
|
externalPort = 80
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
localPort = 38145
|
localPort = 35077
|
||||||
externalPort = 3003
|
externalPort = 3003
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
|
|||||||
@@ -33,8 +33,12 @@ export default function ClientCallsModal({ isOpen, onClose, pendingCalls }: Clie
|
|||||||
description: "L'appel a été enregistré avec succès",
|
description: "L'appel a été enregistré avec succès",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Invalider les caches
|
// Invalider les caches avec la bonne stratégie
|
||||||
queryClient.invalidateQueries({ queryKey: ['/api/customer-orders/pending-calls'] });
|
queryClient.invalidateQueries({
|
||||||
|
predicate: (query) => {
|
||||||
|
return query.queryKey[0] === '/api/customer-orders/pending-calls';
|
||||||
|
}
|
||||||
|
});
|
||||||
queryClient.invalidateQueries({ queryKey: ['/api/customer-orders'] });
|
queryClient.invalidateQueries({ queryKey: ['/api/customer-orders'] });
|
||||||
},
|
},
|
||||||
onError: (error, customerOrderId) => {
|
onError: (error, customerOrderId) => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -28,7 +29,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Pagination, usePagination } from "@/components/ui/pagination";
|
import { Pagination, usePagination } from "@/components/ui/pagination";
|
||||||
import { Plus, Edit, Trash2, Phone, PhoneCall, Printer, Eye, Package } from "lucide-react";
|
import { Plus, Edit, Trash2, Phone, PhoneCall, Printer, Eye, Package, AlertCircle } from "lucide-react";
|
||||||
import JsBarcode from 'jsbarcode';
|
import JsBarcode from 'jsbarcode';
|
||||||
import { safeFormat, safeDate } from "@/lib/dateUtils";
|
import { safeFormat, safeDate } from "@/lib/dateUtils";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
@@ -39,6 +40,7 @@ import type { CustomerOrderWithRelations, Group } from "@shared/schema";
|
|||||||
import { CustomerOrderForm } from "@/components/CustomerOrderForm";
|
import { CustomerOrderForm } from "@/components/CustomerOrderForm";
|
||||||
import { CustomerOrderDetails } from "@/components/CustomerOrderDetails";
|
import { CustomerOrderDetails } from "@/components/CustomerOrderDetails";
|
||||||
import { ConfirmationModal } from "@/components/ConfirmationModal";
|
import { ConfirmationModal } from "@/components/ConfirmationModal";
|
||||||
|
import ClientCallsModal from "@/components/modals/ClientCallsModal";
|
||||||
import { useStore } from "@/contexts/StoreContext";
|
import { useStore } from "@/contexts/StoreContext";
|
||||||
import { usePermissions } from "@shared/permissions";
|
import { usePermissions } from "@shared/permissions";
|
||||||
|
|
||||||
@@ -52,6 +54,7 @@ export default function CustomerOrders() {
|
|||||||
const [showDetailsModal, setShowDetailsModal] = useState(false);
|
const [showDetailsModal, setShowDetailsModal] = useState(false);
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
const [showStatusModal, setShowStatusModal] = useState(false);
|
const [showStatusModal, setShowStatusModal] = useState(false);
|
||||||
|
const [showClientCallsModal, setShowClientCallsModal] = useState(false);
|
||||||
const [selectedOrder, setSelectedOrder] = useState<CustomerOrderWithRelations | null>(null);
|
const [selectedOrder, setSelectedOrder] = useState<CustomerOrderWithRelations | null>(null);
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [sortBy, setSortBy] = useState<"date" | "status" | "supplier">("date");
|
const [sortBy, setSortBy] = useState<"date" | "status" | "supplier">("date");
|
||||||
@@ -64,6 +67,22 @@ export default function CustomerOrders() {
|
|||||||
queryKey: ['/api/groups'],
|
queryKey: ['/api/groups'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Query pour les appels clients en attente
|
||||||
|
const { data: pendingCalls = [], isLoading: isPendingCallsLoading } = useQuery<CustomerOrderWithRelations[]>({
|
||||||
|
queryKey: ['/api/customer-orders/pending-calls', selectedStoreId],
|
||||||
|
queryFn: async () => {
|
||||||
|
const pendingCallsUrl = `/api/customer-orders/pending-calls${selectedStoreId ? `?storeId=${selectedStoreId}` : ''}`;
|
||||||
|
const response = await fetch(pendingCallsUrl, { credentials: 'include' });
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch pending calls');
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('📞 Customer Orders - Pending calls received:', Array.isArray(data) ? data.length : 'NOT_ARRAY', 'items');
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
refetchInterval: 30000, // Refetch every 30 seconds
|
||||||
|
});
|
||||||
|
|
||||||
// Fetch suppliers for filter
|
// Fetch suppliers for filter
|
||||||
const { data: suppliers = [] } = useQuery<any[]>({
|
const { data: suppliers = [] } = useQuery<any[]>({
|
||||||
queryKey: ['/api/suppliers'],
|
queryKey: ['/api/suppliers'],
|
||||||
@@ -688,6 +707,23 @@ export default function CustomerOrders() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6 space-y-6">
|
<div className="p-6 space-y-6">
|
||||||
|
{/* Alerte pour les appels clients en attente */}
|
||||||
|
{!isPendingCallsLoading && pendingCalls.length > 0 && (
|
||||||
|
<Alert
|
||||||
|
className="sticky top-0 z-50 bg-orange-50 border-orange-200 cursor-pointer hover:bg-orange-100 transition-colors"
|
||||||
|
onClick={() => setShowClientCallsModal(true)}
|
||||||
|
data-testid="alert-client-calls"
|
||||||
|
>
|
||||||
|
<Phone className="h-4 w-4 text-orange-600" />
|
||||||
|
<AlertDescription className="text-orange-700">
|
||||||
|
<strong>📞 {pendingCalls.length} client{pendingCalls.length > 1 ? 's' : ''} à appeler</strong>
|
||||||
|
<span className="block text-sm mt-1">
|
||||||
|
Produits disponibles - Cliquez pour voir la liste des clients à contacter
|
||||||
|
</span>
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="bg-white border-b border-gray-200 p-6 shadow-sm -m-6 mb-6">
|
<div className="bg-white border-b border-gray-200 p-6 shadow-sm -m-6 mb-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
@@ -996,6 +1032,13 @@ export default function CustomerOrders() {
|
|||||||
confirmText="Supprimer"
|
confirmText="Supprimer"
|
||||||
isLoading={deleteMutation.isPending}
|
isLoading={deleteMutation.isPending}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Modal pour les appels clients */}
|
||||||
|
<ClientCallsModal
|
||||||
|
isOpen={showClientCallsModal}
|
||||||
|
onClose={() => setShowClientCallsModal(false)}
|
||||||
|
pendingCalls={pendingCalls}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+7
-11
@@ -1682,8 +1682,8 @@ export class DatabaseStorage implements IStorage {
|
|||||||
|
|
||||||
async getPendingClientCalls(groupIds?: number[]): Promise<CustomerOrderWithRelations[]> {
|
async getPendingClientCalls(groupIds?: number[]): Promise<CustomerOrderWithRelations[]> {
|
||||||
const conditions = [
|
const conditions = [
|
||||||
eq(dlcProducts.status, 'valides'),
|
eq(customerOrders.customerNotified, false),
|
||||||
eq(customerOrders.customerNotified, false)
|
inArray(customerOrders.status, ['Disponible', 'disponible', 'Arrivé', 'arrivé', 'Prêt', 'prêt'])
|
||||||
];
|
];
|
||||||
|
|
||||||
if (groupIds && groupIds.length > 0) {
|
if (groupIds && groupIds.length > 0) {
|
||||||
@@ -1697,7 +1697,6 @@ export class DatabaseStorage implements IStorage {
|
|||||||
group: groups
|
group: groups
|
||||||
})
|
})
|
||||||
.from(customerOrders)
|
.from(customerOrders)
|
||||||
.innerJoin(dlcProducts, eq(customerOrders.gencode, dlcProducts.gencode))
|
|
||||||
.leftJoin(suppliers, eq(customerOrders.supplierId, suppliers.id))
|
.leftJoin(suppliers, eq(customerOrders.supplierId, suppliers.id))
|
||||||
.leftJoin(groups, eq(customerOrders.groupId, groups.id))
|
.leftJoin(groups, eq(customerOrders.groupId, groups.id))
|
||||||
.where(and(...conditions))
|
.where(and(...conditions))
|
||||||
@@ -3477,15 +3476,12 @@ export class MemStorage implements IStorage {
|
|||||||
orders = orders.filter(order => groupIds.includes(order.groupId));
|
orders = orders.filter(order => groupIds.includes(order.groupId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find orders where client not notified and product is available
|
// Find orders where client not notified and status is 'Disponible'
|
||||||
const pendingOrders = orders.filter(order => {
|
const pendingOrders = orders.filter(order => {
|
||||||
if (order.customerNotified) return false;
|
return !order.customerNotified &&
|
||||||
|
(order.status === 'Disponible' || order.status === 'disponible' ||
|
||||||
// Check if there's a DLC product with status 'valides' matching this gencode
|
order.status === 'Arrivé' || order.status === 'arrivé' ||
|
||||||
const matchingDlcProduct = Array.from(this.dlcProducts.values())
|
order.status === 'Prêt' || order.status === 'prêt');
|
||||||
.find(dlc => dlc.gencode === order.gencode && dlc.status === 'valides');
|
|
||||||
|
|
||||||
return !!matchingDlcProduct;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return pendingOrders.map(order => ({
|
return pendingOrders.map(order => ({
|
||||||
|
|||||||
Reference in new issue
Block a user