From 7a534e4fc341b56a4f79b6df18befe0b49703ade Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Mon, 11 Aug 2025 21:37:47 +0000 Subject: [PATCH] Enhance calendar view with detailed order and delivery item display Add a new `CalendarItem` component to visually represent orders and deliveries in the calendar grid, including status indicators and click handlers. Implement a `DayItemsContainer` to manage and display multiple items per day, with overflow handling via a popover. Replit-Commit-Author: Agent Replit-Commit-Session-Id: b163d4c0-de5e-4f4e-a9c0-aed4c7049718 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/b163d4c0-de5e-4f4e-a9c0-aed4c7049718/2ecSf3X --- client/src/components/CalendarGrid.tsx | 223 +++++++++++++++++-------- 1 file changed, 152 insertions(+), 71 deletions(-) diff --git a/client/src/components/CalendarGrid.tsx b/client/src/components/CalendarGrid.tsx index 7eb7caf..fa86377 100644 --- a/client/src/components/CalendarGrid.tsx +++ b/client/src/components/CalendarGrid.tsx @@ -1,8 +1,10 @@ import { format, startOfMonth, endOfMonth, eachDayOfInterval, isSameDay, isSameMonth, isToday } from "date-fns"; import { fr } from "date-fns/locale"; import { safeDate } from "@/lib/dateUtils"; -import { Plus, Check } from "lucide-react"; +import { Plus, Check, MoreHorizontal } from "lucide-react"; import { Button } from "@/components/ui/button"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { useState } from "react"; import type { OrderWithRelations, DeliveryWithRelations } from "@shared/schema"; interface CalendarGridProps { @@ -13,6 +15,149 @@ interface CalendarGridProps { onItemClick: (item: any, type: 'order' | 'delivery') => void; } +// Composant pour afficher un élément (commande ou livraison) +function CalendarItem({ item, type, onItemClick }: { item: any, type: 'order' | 'delivery', onItemClick: (item: any, type: 'order' | 'delivery') => void }) { + const formatQuantity = (quantity: number, unit: string) => { + return `${quantity}${unit === 'palettes' ? 'P' : 'C'}`; + }; + + if (type === 'order') { + const colorClass = item.status === 'delivered' + ? 'bg-delivered text-white' + : item.status === 'planned' + ? 'bg-orange-500 text-white border-2 border-orange-300' + : 'bg-primary text-white'; + + return ( +
{ + e.stopPropagation(); + onItemClick(item, 'order'); + }} + > + + {item.supplier.name} + +
+ {item.status === 'planned' && ( + + )} + {item.status === 'delivered' && ( + + )} +
+
+ ); + } + + // Livraison + return ( +
{ + e.stopPropagation(); + onItemClick(item, 'delivery'); + }} + > + + {item.supplier.name} - {formatQuantity(item.quantity, item.unit)} + +
+ {item.status === 'pending' && ( + + )} + {item.status === 'delivered' && ( + + )} +
+
+ ); +} + +// Composant pour gérer l'overflow avec popover +function DayItemsContainer({ dayOrders, dayDeliveries, onItemClick }: { dayOrders: any[], dayDeliveries: any[], onItemClick: (item: any, type: 'order' | 'delivery') => void }) { + const [isOpen, setIsOpen] = useState(false); + const MAX_VISIBLE_ITEMS = 2; + const totalItems = dayOrders.length + dayDeliveries.length; + + if (totalItems === 0) return null; + + // Combiner tous les éléments avec leur type + const allItems = [ + ...dayOrders.map(order => ({ ...order, itemType: 'order' as const })), + ...dayDeliveries.map(delivery => ({ ...delivery, itemType: 'delivery' as const })) + ]; + + const visibleItems = allItems.slice(0, MAX_VISIBLE_ITEMS); + const hiddenCount = Math.max(0, totalItems - MAX_VISIBLE_ITEMS); + + return ( +
+ {/* Éléments visibles */} + {visibleItems.map((item, index) => ( + + ))} + + {/* Badge "+X autres" avec popover */} + {hiddenCount > 0 && ( + + + + + e.stopPropagation()} + > +
+

+ Tous les éléments ({totalItems}) +

+
+ {allItems.map((item) => ( + { + onItemClick(clickedItem, type); + setIsOpen(false); + }} + /> + ))} +
+
+
+
+ )} +
+ ); +} + export default function CalendarGrid({ currentDate, orders, @@ -118,10 +263,6 @@ export default function CalendarGrid({ return { orders: dayOrders, deliveries: dayDeliveries }; }; - const formatQuantity = (quantity: number, unit: string) => { - return `${quantity}${unit === 'palettes' ? 'P' : 'C'}`; - }; - return (
{/* Calendar Header */} @@ -161,72 +302,12 @@ export default function CalendarGrid({ {format(date, 'd')} - {/* Orders and Deliveries */} -
- {dayOrders.map((order) => { - // Vérifier si la commande a une livraison liée (peu importe le statut) - const hasLinkedDelivery = order.deliveries && order.deliveries.length > 0; - - const colorClass = order.status === 'delivered' - ? 'bg-delivered text-white' - : order.status === 'planned' - ? 'bg-orange-500 text-white border-2 border-orange-300' - : 'bg-primary text-white'; - - return ( -
{ - e.stopPropagation(); - onItemClick(order, 'order'); - }} - > - - {order.supplier.name} - -
- - {order.status === 'planned' && ( - - )} - {order.status === 'delivered' && ( - - )} -
-
- ); - })} - - {dayDeliveries.map((delivery) => ( -
{ - e.stopPropagation(); - onItemClick(delivery, 'delivery'); - }} - > - - {delivery.supplier.name} - {formatQuantity(delivery.quantity, delivery.unit)} - -
- {delivery.status === 'pending' && ( - - )} - {delivery.status === 'delivered' && ( - - )} -
-
- ))} -
+ {/* Orders and Deliveries avec système d'overflow */} +
{/* Quick Create Button */}