diff --git a/.replit b/.replit
index 6b5c189..cf77b29 100644
--- a/.replit
+++ b/.replit
@@ -19,6 +19,10 @@ externalPort = 80
localPort = 33035
externalPort = 3001
+[[ports]]
+localPort = 35023
+externalPort = 6000
+
[[ports]]
localPort = 41295
externalPort = 5173
diff --git a/client/src/pages/planning-mobile.tsx b/client/src/pages/planning-mobile.tsx
index 0e3e8e3..94e4069 100644
--- a/client/src/pages/planning-mobile.tsx
+++ b/client/src/pages/planning-mobile.tsx
@@ -6,8 +6,25 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Badge } from "@/components/ui/badge";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
-import { Calendar, MapPin, User, Car, Clock, Navigation, ListOrdered, Copy } from "lucide-react";
+import { Calendar, MapPin, User, Car, Clock, Navigation, ListOrdered, Copy, GripVertical } from "lucide-react";
import { format, parseISO, isValid, addDays } from "date-fns";
+import {
+ DndContext,
+ closestCenter,
+ KeyboardSensor,
+ PointerSensor,
+ useSensor,
+ useSensors,
+ DragEndEvent,
+} from '@dnd-kit/core';
+import {
+ arrayMove,
+ SortableContext,
+ sortableKeyboardCoordinates,
+ verticalListSortingStrategy,
+ useSortable,
+} from '@dnd-kit/sortable';
+import { CSS } from '@dnd-kit/utilities';
import {
Dialog,
DialogContent,
@@ -61,6 +78,50 @@ type MobileSite = {
longitude: string | null;
};
+// Componente per tappa draggable
+function SortableStop({ site, index, onRemove }: { site: MobileSite; index: number; onRemove: () => void }) {
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ transform,
+ transition,
+ isDragging,
+ } = useSortable({ id: site.id });
+
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ opacity: isDragging ? 0.5 : 1,
+ };
+
+ return (
+
+
+
+
+
+ {index + 1}
+
+
{site.name}
+
+
+ );
+}
+
type AvailableGuard = {
id: string;
firstName: string;
@@ -111,6 +172,27 @@ export default function PlanningMobile() {
// Ref per scroll alla sezione sequenze pattuglia
const patrolSequencesRef = useRef(null);
+ // Sensors per drag-and-drop
+ const sensors = useSensors(
+ useSensor(PointerSensor),
+ useSensor(KeyboardSensor, {
+ coordinateGetter: sortableKeyboardCoordinates,
+ })
+ );
+
+ // Handler per riordinare le tappe con drag-and-drop
+ const handleDragEnd = (event: DragEndEvent) => {
+ const { active, over } = event;
+
+ if (over && active.id !== over.id) {
+ setPatrolRoute((items) => {
+ const oldIndex = items.findIndex(item => item.id === active.id);
+ const newIndex = items.findIndex(item => item.id === over.id);
+ return arrayMove(items, oldIndex, newIndex);
+ });
+ }
+ };
+
// Query siti mobile per location
const { data: mobileSites, isLoading: sitesLoading } = useQuery({
queryKey: ["/api/planning-mobile/sites", selectedLocation],
@@ -562,32 +644,32 @@ export default function PlanningMobile() {
{patrolRoute.length} tappe programmate per il turno del {format(parseISO(selectedDate), "dd MMMM yyyy", { locale: it })}
+
+ 💡 Trascina le tappe per riordinarle
-
- {patrolRoute.map((site, index) => (
-
-
- {index + 1}
-
-
{site.name}
-
+
+ site.id)}
+ strategy={verticalListSortingStrategy}
+ >
+
+ {patrolRoute.map((site, index) => (
+ handleRemoveFromRoute(site.id)}
+ />
+ ))}
- ))}
-
+
+