Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions frontend/Front.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,22 @@ export default function PickupDeliveryUI() {
if (!deliveryRequestSet) return null;
if (selectedCourierId === null) return deliveryRequestSet;

const targetId = String(selectedCourierId);
const assignments = effectiveAssignments || {};

if (selectedCourierId === 'unassigned') {
const filteredDemands = (deliveryRequestSet.demands || []).filter((demand) => {
const assignedCourierId = assignments[demand.id];
return assignedCourierId === null || assignedCourierId === undefined;
});

return {
...deliveryRequestSet,
demands: filteredDemands,
};
}

const targetId = String(selectedCourierId);

const filteredDemands = (deliveryRequestSet.demands || []).filter((demand) => {
const assignedCourierId = assignments[demand.id];
if (assignedCourierId === null || assignedCourierId === undefined) {
Expand Down Expand Up @@ -574,11 +587,14 @@ export default function PickupDeliveryUI() {

try {
setIsCalculatingTour(true);
const currentAssignment = demandAssignments?.[demandId];
const contextCourier =
selectedCourierId === 'unassigned' ? null : selectedCourierId;
await apiService.updateCourierAssignment({
demandId,
newCourierId: targetCourierId !== null && targetCourierId !== undefined ? String(targetCourierId) : null,
oldCourierId: (demandAssignments?.[demandId] ?? selectedCourierId ?? null) !== null
? String(demandAssignments?.[demandId] ?? selectedCourierId)
oldCourierId: (currentAssignment ?? contextCourier ?? null) !== null
? String(currentAssignment ?? contextCourier)
: null,
deliveryIndex: null,
});
Expand Down Expand Up @@ -1267,7 +1283,7 @@ export default function PickupDeliveryUI() {
<TourTabs
tours={tourData}
deliveryRequestSet={filteredDeliveryRequestSet}
onTourSelect={(tour) => setSelectedCourierId(tour?.courierId || null)}
onTourSelect={(tour) => setSelectedCourierId(tour?.courierId ?? null)}
demandAssignments={effectiveAssignments}
unassignedDemands={effectiveUnassignedDemands}
onReassignDemand={handleReassignDemand}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/Icon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const iconMap = {
warning: faTriangleExclamation,
success: faCircleCheck,
error: faCircleXmark,
na: faCircleXmark,
'x-circle': faCircleXmark,
info: faCircleInfo,
rocket: faRocket,
box: faBox,
Expand Down
54 changes: 52 additions & 2 deletions frontend/src/components/TourTabs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export default function TourTabs({
if (onTourSelect) {
if (courierId === null) {
onTourSelect(null); // Vue globale : afficher tous les tours
} else if (courierId === 'unassigned') {
onTourSelect({ courierId: 'unassigned' });
} else {
const selectedTour = displayTours.find(t => t.courierId === courierId);
onTourSelect(selectedTour);
Expand Down Expand Up @@ -142,6 +144,19 @@ export default function TourTabs({
return Array.from(union.values());
}, [allDemands, demandAssignments, unassignedDemands]);

const hasUnassigned = derivedUnassigned.length > 0;

// Si l'onglet N/A est sélectionné mais qu'il n'y a plus de demandes non assignées,
// revenir automatiquement à la vue globale pour éviter un onglet vide.
useEffect(() => {
if (selectedCourierId === 'unassigned' && !hasUnassigned) {
setSelectedCourierId(null);
if (onTourSelect) {
onTourSelect(null);
}
}
}, [selectedCourierId, hasUnassigned, onTourSelect]);

const demandsForCourier = (courierId) =>
allDemands.filter((d) => (demandAssignments?.[d.id] ?? null) === courierId);

Expand Down Expand Up @@ -184,9 +199,12 @@ export default function TourTabs({
setShowAssignModal(false);
};

const handleRemove = async (demand) => {
const handleRemove = async (demandOrId) => {
if (!onRemoveDemand) return;
await onRemoveDemand(demand.id);
const demandId =
demandOrId && typeof demandOrId === 'object' ? demandOrId.id : demandOrId;
if (!demandId) return;
await onRemoveDemand(demandId);
};

return (
Expand Down Expand Up @@ -234,6 +252,22 @@ export default function TourTabs({
Vue globale
</span>
</button>
{/* Onglet Non assignées (affiché uniquement s'il existe des demandes non assignées) */}
{hasUnassigned && (
<button
onClick={() => handleTabClick('unassigned')}
className={`px-4 py-3 font-medium whitespace-nowrap transition-colors flex-shrink-0 ${
selectedCourierId === 'unassigned'
? 'border-b-2 border-blue-500 text-blue-400'
: 'text-gray-400 hover:text-white hover:bg-gray-700/50'
}`}
>
<span className="inline-flex items-center gap-2">
<Icon name="na" className="text-blue-400" />
N/A
</span>
</button>
)}

{/* Onglets par coursier */}
{displayTours.map(tour => (
Expand Down Expand Up @@ -278,6 +312,22 @@ export default function TourTabs({
/>
)}
</div>
) : selectedCourierId === 'unassigned' && hasUnassigned ? (
<div className="space-y-4">
<div className="flex items-center gap-2">
<span className="w-3 h-3 rounded-full bg-gray-500" />
<h3 className="text-lg font-semibold text-white">Demandes non assignées</h3>
</div>
<DemandAssignmentTable
demands={derivedUnassigned}
assignments={demandAssignments}
courierOptions={courierOptions}
onReassign={handleRequestReassign}
onRemove={handleRemove}
isBusy={isBusy}
emptyMessage="Aucune demande non assignée"
/>
</div>
) : (
<div className="space-y-4">
<div className="flex items-center justify-between">
Expand Down