Add button for admins to validate orders directly from the calendar

Adds a conditional "Validate Order" button (visible on hover to admins only) to the order cards in the calendar view, allowing for one-click status updates to 'delivered' and invalidating related data queries.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: a8a78c07-e900-425c-a577-5b4c5894379d
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/a8a78c07-e900-425c-a577-5b4c5894379d/3ec1Xks
This commit is contained in:
michaelschal committed 2025-08-11 21:01:08 +00:00
1 parent 041891baf0
commit a8ff4c0c73
3 files changed
+117 -4

No files matched your search

+54
View File
@@ -0,0 +1,54 @@
# Fonctionnalité Bouton "Valider Commande" - Calendrier
## Nouvelle Fonctionnalité
J'ai ajouté un bouton de validation directement sur les cartes des commandes dans le calendrier, avec les caractéristiques suivantes :
### Fonctionnement
- **Visibilité** : Le bouton n'est visible que pour les utilisateurs avec le rôle `admin`
- **Apparition** : Le bouton apparaît uniquement au survol de la carte de commande (effet hover)
- **Cible** : Seules les commandes qui ne sont pas encore en statut `delivered` affichent le bouton
- **Action** : Un clic sur le bouton marque immédiatement la commande comme `delivered`
### Interface Utilisateur
- **Icône** : CheckCircle (cercle avec coche)
- **Position** : À droite de la carte, avant les autres indicateurs de statut
- **Style** : Bouton fantôme semi-transparent qui devient visible au survol
- **Feedback** : Toast de confirmation quand la validation réussit
### Avantages
1. **Accès Direct** : Plus besoin d'ouvrir le modal de détail pour valider une commande
2. **Interface Propre** : Le bouton n'encombre pas l'interface car il n'apparaît qu'au survol
3. **Sécurité** : Réservé aux administrateurs uniquement
4. **Efficacité** : Validation en un clic avec feedback immédiat
### Utilisation
1. **Connexion Admin** : Connectez-vous avec un compte administrateur
2. **Survol** : Passez la souris sur une commande non-delivered (orange ou bleue)
3. **Validation** : Cliquez sur l'icône de cercle avec coche qui apparaît
4. **Confirmation** : La commande passe en gris (delivered) avec un toast de confirmation
### Déploiement en Production
Les modifications ont été apportées aux fichiers :
- `client/src/components/CalendarGrid.tsx` : Ajout du bouton et logique de validation
- `client/src/pages/Calendar.tsx` : Passage des props user et callback de rafraîchissement
Pour déployer en production :
1. Appliquer les modifications aux fichiers mentionnés
2. Reconstruire le frontend (`npm run build`)
3. Redémarrer l'application
### Résolution du Problème CMD-55
Avec cette fonctionnalité, vous pouvez maintenant :
1. Trouver CMD-55 dans le calendrier
2. Passer la souris dessus (si elle n'est pas déjà delivered)
3. Cliquer sur le bouton de validation
4. CMD-55 passera automatiquement en gris (delivered)
Cette solution est plus simple et directe que la synchronisation manuelle précédente.
+58 -4
View File
@@ -1,8 +1,9 @@
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, CheckCircle } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
import type { OrderWithRelations, DeliveryWithRelations } from "@shared/schema";
interface CalendarGridProps {
@@ -11,6 +12,8 @@ interface CalendarGridProps {
deliveries: DeliveryWithRelations[];
onDateClick: (date: Date) => void;
onItemClick: (item: any, type: 'order' | 'delivery') => void;
user?: { role: string } | null;
onOrderValidated?: () => void;
}
export default function CalendarGrid({
@@ -19,7 +22,10 @@ export default function CalendarGrid({
deliveries,
onDateClick,
onItemClick,
user,
onOrderValidated,
}: CalendarGridProps) {
const { toast } = useToast();
const monthStart = startOfMonth(currentDate);
const monthEnd = endOfMonth(currentDate);
@@ -54,6 +60,41 @@ export default function CalendarGrid({
const weekDays = ["Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"];
// Fonction pour valider une commande
const handleValidateOrder = async (orderId: number, event: React.MouseEvent) => {
event.stopPropagation();
try {
const response = await fetch(`/api/orders/${orderId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ status: 'delivered' })
});
if (!response.ok) {
throw new Error(`Erreur ${response.status}: ${await response.text()}`);
}
toast({
title: "Commande validée",
description: `La commande CMD-${orderId} a été marquée comme livrée.`,
});
// Rafraîchir les données
if (onOrderValidated) {
onOrderValidated();
}
} catch (error) {
console.error('Erreur lors de la validation:', error);
toast({
title: "Erreur",
description: "Impossible de valider la commande.",
variant: "destructive"
});
}
};
const getItemsForDate = (date: Date) => {
// Debug: Log des commandes reçues (seulement une fois)
if (orders.length > 0 && date.getDate() === 1) {
@@ -77,7 +118,7 @@ export default function CalendarGrid({
}
// Essayer plusieurs champs de date possibles
const orderDate = safeDate(order.plannedDate || order.orderDate || order.createdAt);
const orderDate = safeDate(order.plannedDate || order.createdAt);
const matches = orderDate && isSameDay(orderDate, date);
if (matches) {
@@ -100,7 +141,7 @@ export default function CalendarGrid({
}
// Essayer plusieurs champs de date possibles
const deliveryDate = safeDate(delivery.scheduledDate || delivery.deliveryDate || delivery.createdAt);
const deliveryDate = safeDate(delivery.scheduledDate || delivery.deliveredDate || delivery.createdAt);
const matches = deliveryDate && isSameDay(deliveryDate, date);
if (matches) {
@@ -176,7 +217,7 @@ export default function CalendarGrid({
return (
<div
key={`order-${order.id}`}
className={`text-xs px-2 py-1 flex items-center justify-between cursor-pointer ${colorClass}`}
className={`text-xs px-2 py-1 flex items-center justify-between cursor-pointer group/order ${colorClass}`}
onClick={(e) => {
e.stopPropagation();
onItemClick(order, 'order');
@@ -186,6 +227,19 @@ export default function CalendarGrid({
{order.supplier.name}
</span>
<div className="flex items-center ml-1 flex-shrink-0">
{/* Bouton de validation pour les admins si la commande n'est pas delivered */}
{user?.role === 'admin' && order.status !== 'delivered' && (
<Button
size="sm"
variant="ghost"
className="h-5 w-5 p-0 hover:bg-white/20 opacity-0 group-hover/order:opacity-100 transition-opacity mr-1"
onClick={(e) => handleValidateOrder(order.id, e)}
title="Valider la commande"
>
<CheckCircle className="w-3 h-3 text-green-200" />
</Button>
)}
{order.status === 'planned' && (
<span className="w-2 h-2 bg-yellow-300 mr-1" title="Commande planifiée (liée à une livraison)" />
)}
+5
View File
@@ -228,6 +228,11 @@ export default function Calendar() {
deliveries={deliveries}
onDateClick={handleDateClick}
onItemClick={handleItemClick}
user={user}
onOrderValidated={() => {
queryClient.invalidateQueries({ queryKey: ['/api/orders'] });
queryClient.invalidateQueries({ queryKey: ['/api/deliveries'] });
}}
/>
)}
</div>