From 17119cda9960a4ecf6ace0c9bf63e9545cdd9f64 Mon Sep 17 00:00:00 2001 From: AV77web Date: Fri, 29 May 2026 16:50:53 +0200 Subject: [PATCH] modifiche a Modale di scarico, inserito Drag & Drop e modale destinazione scarico --- .../[idmacchina]/[idart]/route.ts | 57 ++++ .../art-macchine-parti/[idmacchina]/route.ts | 34 +++ .../api/art-macchine-parti/assegna/route.ts | 46 +++ src/app/api/art-macchine-parti/route.ts | 23 ++ src/components/ArticleCard.tsx | 32 ++- src/components/ArticlesList.tsx | 11 + src/components/DestinazioneModal.tsx | 240 ++++++++++++++++ src/components/MacchineTree.tsx | 269 +++++++++++++++++- src/components/MovimentoModal.tsx | 209 ++++++++++---- src/components/TreeNode.tsx | 5 +- src/lib/artMacchineParti/index.ts | 172 +++++++++++ src/types/artMacchineParti.ts | 37 +++ 12 files changed, 1069 insertions(+), 66 deletions(-) create mode 100644 src/app/api/art-macchine-parti/[idmacchina]/[idart]/route.ts create mode 100644 src/app/api/art-macchine-parti/[idmacchina]/route.ts create mode 100644 src/app/api/art-macchine-parti/assegna/route.ts create mode 100644 src/app/api/art-macchine-parti/route.ts create mode 100644 src/components/DestinazioneModal.tsx create mode 100644 src/lib/artMacchineParti/index.ts create mode 100644 src/types/artMacchineParti.ts diff --git a/src/app/api/art-macchine-parti/[idmacchina]/[idart]/route.ts b/src/app/api/art-macchine-parti/[idmacchina]/[idart]/route.ts new file mode 100644 index 0000000..6944436 --- /dev/null +++ b/src/app/api/art-macchine-parti/[idmacchina]/[idart]/route.ts @@ -0,0 +1,57 @@ +import { NextResponse } from "next/server"; + +import { auth } from "@/src/auth"; +import { rimuoviArticoloDaMacchina } from "@/src/lib/artMacchineParti"; + +interface RouteParams { + params: Promise<{ idmacchina: string; idart: string }>; +} + +export async function DELETE(request: Request, { params }: RouteParams) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + const { idmacchina, idart } = await params; + const macchinaId = parseInt(idmacchina, 10); + const articoloId = parseInt(idart, 10); + + if (isNaN(macchinaId) || isNaN(articoloId)) { + return NextResponse.json({ error: "ID non validi" }, { status: 400 }); + } + + try { + const body = await request.json(); + const { quantita, opzione } = body; + + if (!quantita || quantita <= 0) { + return NextResponse.json( + { error: "Quantità non valida" }, + { status: 400 } + ); + } + + if (!opzione || !["carico", "elimina"].includes(opzione)) { + return NextResponse.json( + { error: "Opzione non valida (carico o elimina)" }, + { status: 400 } + ); + } + + const result = await rimuoviArticoloDaMacchina( + articoloId, + macchinaId, + quantita, + opzione, + session.user.email || session.user.name || "unknown" + ); + + return NextResponse.json(result); + } catch (error) { + console.error("[API art-macchine-parti/:idmacchina/:idart] Errore DELETE:", error); + const message = error instanceof Error ? error.message : "Errore nella rimozione"; + return NextResponse.json({ error: message }, { status: 500 }); + } +} diff --git a/src/app/api/art-macchine-parti/[idmacchina]/route.ts b/src/app/api/art-macchine-parti/[idmacchina]/route.ts new file mode 100644 index 0000000..b3ea273 --- /dev/null +++ b/src/app/api/art-macchine-parti/[idmacchina]/route.ts @@ -0,0 +1,34 @@ +import { NextResponse } from "next/server"; + +import { auth } from "@/src/auth"; +import { getArticoliByMacchina } from "@/src/lib/artMacchineParti"; + +interface RouteParams { + params: Promise<{ idmacchina: string }>; +} + +export async function GET(request: Request, { params }: RouteParams) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + const { idmacchina } = await params; + const macchinaId = parseInt(idmacchina, 10); + + if (isNaN(macchinaId)) { + return NextResponse.json({ error: "ID macchina non valido" }, { status: 400 }); + } + + try { + const articoli = await getArticoliByMacchina(macchinaId); + return NextResponse.json({ articoli }); + } catch (error) { + console.error("[API art-macchine-parti/:idmacchina] Errore GET:", error); + return NextResponse.json( + { error: "Errore nel recupero degli articoli" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/art-macchine-parti/assegna/route.ts b/src/app/api/art-macchine-parti/assegna/route.ts new file mode 100644 index 0000000..bc51065 --- /dev/null +++ b/src/app/api/art-macchine-parti/assegna/route.ts @@ -0,0 +1,46 @@ +import { NextResponse } from "next/server"; + +import { auth } from "@/src/auth"; +import { assegnaArticoloMacchina } from "@/src/lib/artMacchineParti"; + +export async function POST(request: Request) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + const body = await request.json(); + + const { idart, idmacchina, quantita, causale } = body; + + if (!idart || !idmacchina || !quantita) { + return NextResponse.json( + { error: "Parametri mancanti: idart, idmacchina, quantita sono obbligatori" }, + { status: 400 } + ); + } + + if (quantita <= 0) { + return NextResponse.json( + { error: "La quantità deve essere maggiore di zero" }, + { status: 400 } + ); + } + + const result = await assegnaArticoloMacchina({ + idart: parseInt(idart), + idmacchina: parseInt(idmacchina), + quantita: parseInt(quantita), + causale: causale || "Assegnazione articolo a macchina", + user: session.user.email || session.user.name || "unknown", + }); + + return NextResponse.json(result); + } catch (error) { + console.error("[API art-macchine-parti/assegna] Errore POST:", error); + const message = error instanceof Error ? error.message : "Errore nell'assegnazione"; + return NextResponse.json({ error: message }, { status: 500 }); + } +} diff --git a/src/app/api/art-macchine-parti/route.ts b/src/app/api/art-macchine-parti/route.ts new file mode 100644 index 0000000..dad45fa --- /dev/null +++ b/src/app/api/art-macchine-parti/route.ts @@ -0,0 +1,23 @@ +import { NextResponse } from "next/server"; + +import { auth } from "@/src/auth"; +import { getArticoliPerTutteLeMacchine } from "@/src/lib/artMacchineParti"; + +export async function GET() { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + const articoliPerMacchina = await getArticoliPerTutteLeMacchine(); + return NextResponse.json({ articoliPerMacchina }); + } catch (error) { + console.error("[API art-macchine-parti] Errore GET:", error); + return NextResponse.json( + { error: "Errore nel recupero degli articoli" }, + { status: 500 } + ); + } +} diff --git a/src/components/ArticleCard.tsx b/src/components/ArticleCard.tsx index bea9a52..144e9f3 100644 --- a/src/components/ArticleCard.tsx +++ b/src/components/ArticleCard.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useState, type DragEvent } from "react"; import type { Article } from "@/src/types/article"; import type { TipoMovimento } from "@/src/types/movimento"; @@ -11,6 +11,7 @@ interface ArticleCardProps { onEdit: (article: Article) => void; onDelete: (article: Article) => void; onMovimento?: (article: Article, tipo: TipoMovimento) => void; + draggable?: boolean; } export default function ArticleCard({ @@ -20,12 +21,15 @@ export default function ArticleCard({ onEdit, onDelete, onMovimento, + draggable = true, }: ArticleCardProps) { const [deleting, setDeleting] = useState(false); + const [isDragging, setIsDragging] = useState(false); const isSottoScorta = article.quantita < article.quantita_minima; const disponibile = article.quantita - article.assegnata_macchine; const hasGiacenzaMagazzino = giacenzaMagazzino !== undefined; + const canDrag = draggable && disponibile > 0; const handleDelete = async () => { if (!confirm(`Eliminare "${article.descrizione}"?`)) return; @@ -42,11 +46,37 @@ export default function ArticleCard({ } }; + const handleDragStart = (e: DragEvent) => { + if (!canDrag) { + e.preventDefault(); + return; + } + setIsDragging(true); + e.dataTransfer.setData("application/json", JSON.stringify({ + id: article.id, + descrizione: article.descrizione, + codice: article.codice, + barcode: article.barcode, + quantita_residua: disponibile, + famiglia: article.famiglia, + })); + e.dataTransfer.effectAllowed = "move"; + }; + + const handleDragEnd = () => { + setIsDragging(false); + }; + return (
{/* Indicatore sotto scorta */} diff --git a/src/components/ArticlesList.tsx b/src/components/ArticlesList.tsx index 436e4e7..16cd846 100644 --- a/src/components/ArticlesList.tsx +++ b/src/components/ArticlesList.tsx @@ -77,6 +77,17 @@ export default function ArticlesList() { fetchGiacenze(); }, [fetchGiacenze]); + useEffect(() => { + const handleArticoliUpdated = () => { + fetchArticles(); + }; + + window.addEventListener("articoli-updated", handleArticoliUpdated); + return () => { + window.removeEventListener("articoli-updated", handleArticoliUpdated); + }; + }, [fetchArticles]); + const handleEdit = (article: Article) => { setEditingArticle(article); setShowModal(true); diff --git a/src/components/DestinazioneModal.tsx b/src/components/DestinazioneModal.tsx new file mode 100644 index 0000000..af3788b --- /dev/null +++ b/src/components/DestinazioneModal.tsx @@ -0,0 +1,240 @@ +"use client"; + +import { useState, useEffect } from "react"; +import type { MacchinaWithDetails } from "@/src/types/macchina"; + +interface DestinazioneModalProps { + isOpen: boolean; + onClose: () => void; + onSelect: (macchina: MacchinaWithDetails) => void; + selectedId?: number | null; +} + +function TreeNodeSelectable({ + node, + allNodes, + level = 0, + selectedId, + onSelect, +}: { + node: MacchinaWithDetails; + allNodes: MacchinaWithDetails[]; + level?: number; + selectedId?: number | null; + onSelect: (node: MacchinaWithDetails) => void; +}) { + const [isExpanded, setIsExpanded] = useState(level < 2); + const children = allNodes.filter((n) => n.id_padre === node.id); + const isSelected = selectedId === node.id; + + const getNodeTypeIcon = () => { + switch (node.tipo) { + case "macchina": return "🔧"; + case "gruppo": return "📁"; + case "parte": return "⚙️"; + default: return "📄"; + } + }; + + const getNodeTypeBg = () => { + if (isSelected) return "bg-blue-600 border-blue-500"; + switch (node.tipo) { + case "macchina": return "bg-blue-900/30 border-blue-700/50 hover:bg-blue-800/40"; + case "gruppo": return "bg-purple-900/30 border-purple-700/50 hover:bg-purple-800/40"; + case "parte": return "bg-zinc-800/50 border-zinc-700/50 hover:bg-zinc-700/50"; + default: return "bg-zinc-800 border-zinc-700 hover:bg-zinc-700"; + } + }; + + return ( +
+
onSelect(node)} + > + {children.length > 0 && ( + + )} + {children.length === 0 &&
} + + {getNodeTypeIcon()} + + {node.nome} + + + {isSelected && ( + + + + )} +
+ + {isExpanded && children.length > 0 && ( +
+ {children.map((child) => ( + + ))} +
+ )} +
+ ); +} + +export default function DestinazioneModal({ + isOpen, + onClose, + onSelect, + selectedId, +}: DestinazioneModalProps) { + const [nodes, setNodes] = useState([]); + const [loading, setLoading] = useState(false); + const [localSelectedId, setLocalSelectedId] = useState(selectedId || null); + const [localSelectedNode, setLocalSelectedNode] = useState(null); + + useEffect(() => { + if (isOpen) { + setLoading(true); + setLocalSelectedId(selectedId || null); + + fetch("/api/macchine") + .then((res) => res.json()) + .then((data) => { + setNodes(data.macchine || []); + if (selectedId) { + const found = data.macchine?.find((n: MacchinaWithDetails) => n.id === selectedId); + setLocalSelectedNode(found || null); + } + }) + .catch((error) => { + console.error("Errore caricamento macchine:", error); + }) + .finally(() => { + setLoading(false); + }); + } + }, [isOpen, selectedId]); + + const handleNodeSelect = (node: MacchinaWithDetails) => { + setLocalSelectedId(node.id); + setLocalSelectedNode(node); + }; + + const handleConfirm = () => { + if (localSelectedNode) { + onSelect(localSelectedNode); + onClose(); + } + }; + + if (!isOpen) return null; + + const rootNodes = nodes.filter((n) => n.id_padre === null); + + return ( +
+
+ +
+ {/* Header */} +
+
+

Seleziona Destinazione

+

Scegli la macchina o parte a cui assegnare l'articolo

+
+ +
+ + {/* Content */} +
+ {loading ? ( +
+ + + + + Caricamento... +
+ ) : rootNodes.length === 0 ? ( +
+ + + +

Nessuna macchina disponibile

+

Aggiungi prima delle macchine nel sistema

+
+ ) : ( +
+ {rootNodes.map((node) => ( + + ))} +
+ )} +
+ + {/* Footer */} +
+ {localSelectedNode ? ( +
+ Selezionato: + {localSelectedNode.nome} +
+ ) : ( +
Nessuna selezione
+ )} + +
+ + +
+
+
+
+ ); +} diff --git a/src/components/MacchineTree.tsx b/src/components/MacchineTree.tsx index 13d4401..bdde112 100644 --- a/src/components/MacchineTree.tsx +++ b/src/components/MacchineTree.tsx @@ -1,14 +1,30 @@ "use client"; import { useState, useEffect, useCallback, useMemo } from "react"; -import type { MacchinaWithDetails, ArticoliPerMacchina } from "@/src/types/macchina"; +import type { MacchinaWithDetails } from "@/src/types/macchina"; +import type { ArticoloAssegnatoMacchina } from "@/src/types/artMacchineParti"; import TreeNode from "./TreeNode"; import MacchinaModal from "./MacchinaModal"; import { useStabilimento } from "@/src/contexts/StabilimentoContext"; +interface DroppedArticle { + id: number; + descrizione: string; + codice: string; + barcode?: string | null; + quantita_residua: number; + famiglia?: string; +} + +interface DropModalState { + macchinaId: number; + macchinaNome: string; + article: DroppedArticle; +} + export default function MacchineTree() { const [nodes, setNodes] = useState([]); - const [articlesByMacchina, setArticlesByMacchina] = useState({}); + const [articlesByMacchina, setArticlesByMacchina] = useState>({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -26,6 +42,17 @@ export default function MacchineTree() { const [deleteConfirm, setDeleteConfirm] = useState<{ id: number; nome: string } | null>(null); + const [dropModal, setDropModal] = useState(null); + const [dropQuantita, setDropQuantita] = useState(1); + const [dropCausale, setDropCausale] = useState(""); + const [dropSaving, setDropSaving] = useState(false); + + const [showArticlesModal, setShowArticlesModal] = useState<{ + macchinaId: number; + macchinaNome: string; + articoli: ArticoloAssegnatoMacchina[]; + } | null>(null); + const fetchNodes = useCallback(async () => { try { const res = await fetch("/api/macchine"); @@ -37,12 +64,23 @@ export default function MacchineTree() { } }, []); + const fetchArticoliPerMacchina = useCallback(async () => { + try { + const res = await fetch("/api/art-macchine-parti"); + if (!res.ok) return; + const data = await res.json(); + setArticlesByMacchina(data.articoliPerMacchina || {}); + } catch (err) { + console.error("Errore caricamento articoli per macchina:", err); + } + }, []); + const loadInitialData = useCallback(async () => { setLoading(true); setError(null); - await fetchNodes(); + await Promise.all([fetchNodes(), fetchArticoliPerMacchina()]); setLoading(false); - }, [fetchNodes]); + }, [fetchNodes, fetchArticoliPerMacchina]); useEffect(() => { loadInitialData(); @@ -184,11 +222,81 @@ export default function MacchineTree() { }; const handleShowArticles = (nodeId: number) => { - console.log("Show articles for node:", nodeId); + const macchina = nodes.find((n) => n.id === nodeId); + if (!macchina) return; + + const articoliNodo = articlesByMacchina[nodeId] || []; + + const getDescendantArticles = (id: number): ArticoloAssegnatoMacchina[] => { + const children = nodes.filter((n) => n.id_padre === id); + let result: ArticoloAssegnatoMacchina[] = []; + for (const child of children) { + const childArticles = articlesByMacchina[child.id] || []; + result = [...result, ...childArticles, ...getDescendantArticles(child.id)]; + } + return result; + }; + + const allArticoli = [...articoliNodo, ...getDescendantArticles(nodeId)]; + + setShowArticlesModal({ + macchinaId: nodeId, + macchinaNome: macchina.nome, + articoli: allArticoli, + }); }; const handleDropArticle = (macchinaId: number, article: unknown) => { - console.log("Drop article on machine:", macchinaId, article); + const droppedArticle = article as DroppedArticle; + if (!droppedArticle || droppedArticle.quantita_residua <= 0) return; + + const macchina = nodes.find((n) => n.id === macchinaId); + if (!macchina) return; + + setDropModal({ + macchinaId, + macchinaNome: macchina.nome, + article: droppedArticle, + }); + setDropQuantita(1); + setDropCausale(""); + }; + + const handleConfirmDrop = async () => { + if (!dropModal) return; + + if (dropQuantita <= 0 || dropQuantita > dropModal.article.quantita_residua) { + return; + } + + setDropSaving(true); + try { + const res = await fetch("/api/art-macchine-parti/assegna", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + idart: dropModal.article.id, + idmacchina: dropModal.macchinaId, + quantita: dropQuantita, + causale: dropCausale || `Assegnazione a ${dropModal.macchinaNome}`, + }), + }); + + if (!res.ok) { + const data = await res.json(); + throw new Error(data.error || "Errore nell'assegnazione"); + } + + await fetchArticoliPerMacchina(); + setDropModal(null); + + window.dispatchEvent(new CustomEvent("articoli-updated")); + } catch (err) { + console.error("Errore assegnazione:", err); + alert(err instanceof Error ? err.message : "Errore nell'assegnazione"); + } finally { + setDropSaving(false); + } }; const filteredNodes = useMemo(() => { @@ -368,6 +476,155 @@ export default function MacchineTree() {
)} + + {/* Show Articles Modal */} + {showArticlesModal && ( +
+
setShowArticlesModal(null)} /> +
+
+
+

Articoli Assegnati

+

{showArticlesModal.macchinaNome}

+
+ +
+ +
+ {showArticlesModal.articoli.length === 0 ? ( +
+ Nessun articolo assegnato +
+ ) : ( +
+ {showArticlesModal.articoli.map((art) => ( +
+
+

{art.descrizione}

+

{art.codice}

+
+
+
{art.quantita}
+
assegnati
+
+
+ ))} +
+ )} +
+ +
+ + Totale: {showArticlesModal.articoli.reduce((sum, a) => sum + a.quantita, 0)} pezzi in {showArticlesModal.articoli.length} articoli + + +
+
+
+ )} + + {/* Drop Article Modal */} + {dropModal && ( +
+
setDropModal(null)} /> +
+
+
+

Assegna Articolo

+

Trascina e rilascia completato

+
+ +
+ +
+ {/* Info articolo */} +
+

Articolo

+

{dropModal.article.descrizione}

+

{dropModal.article.codice}

+

+ Disponibili: {dropModal.article.quantita_residua} +

+
+ + {/* Info destinazione */} +
+

Destinazione

+

{dropModal.macchinaNome}

+
+ + {/* Quantità */} +
+ + setDropQuantita(Math.max(1, Math.min(dropModal.article.quantita_residua, parseInt(e.target.value) || 1)))} + min={1} + max={dropModal.article.quantita_residua} + className="w-full px-4 py-2 rounded-lg bg-zinc-800 border border-zinc-700 text-white text-lg font-semibold focus:outline-none focus:border-blue-500" + /> +
+ + {/* Causale */} +
+ + setDropCausale(e.target.value)} + placeholder="Es: Manutenzione programmata" + className="w-full px-4 py-2 rounded-lg bg-zinc-800 border border-zinc-700 text-white placeholder-zinc-500 focus:outline-none focus:border-blue-500" + /> +
+
+ +
+ + +
+
+
+ )}
); } diff --git a/src/components/MovimentoModal.tsx b/src/components/MovimentoModal.tsx index 580ab81..6560988 100644 --- a/src/components/MovimentoModal.tsx +++ b/src/components/MovimentoModal.tsx @@ -4,6 +4,8 @@ import { useState, useEffect, useRef } from "react"; import type { Article } from "@/src/types/article"; import type { Stabilimento, Magazzino } from "@/src/types/stabilimento"; import type { TipoMovimento } from "@/src/types/movimento"; +import type { MacchinaWithDetails } from "@/src/types/macchina"; +import DestinazioneModal from "./DestinazioneModal"; interface MovimentoModalProps { article: Article; @@ -28,11 +30,16 @@ export default function MovimentoModal({ const [quantita, setQuantita] = useState(1); const [causale, setCausale] = useState(""); + const [selectedDestinazione, setSelectedDestinazione] = useState(null); + const [showDestinazioneModal, setShowDestinazioneModal] = useState(false); + const [saving, setSaving] = useState(false); const [error, setError] = useState(null); useEffect(() => { async function fetchData() { + if (!isCarico) return; + try { const [stabRes, magRes] = await Promise.all([ fetch("/api/stabilimenti"), @@ -51,7 +58,7 @@ export default function MovimentoModal({ } fetchData(); - }, []); + }, [isCarico]); useEffect(() => { function handleKeyDown(e: KeyboardEvent) { @@ -81,9 +88,16 @@ export default function MovimentoModal({ e.preventDefault(); setError(null); - if (!selectedMagazzino) { - setError("Seleziona un magazzino"); - return; + if (isCarico) { + if (!selectedMagazzino) { + setError("Seleziona un magazzino"); + return; + } + } else { + if (!selectedDestinazione) { + setError("Seleziona una destinazione (macchina/parte)"); + return; + } } if (quantita <= 0) { @@ -91,24 +105,47 @@ export default function MovimentoModal({ return; } + if (!isCarico && quantita > article.quantita) { + setError(`Quantità richiesta (${quantita}) supera la giacenza disponibile (${article.quantita})`); + return; + } + setSaving(true); try { - const res = await fetch("/api/movimenti", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - id_articolo: article.id, - quantita, - tipo, - id_magazzino: selectedMagazzino, - causale: causale || undefined, - }), - }); + if (isCarico) { + const res = await fetch("/api/movimenti", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + id_articolo: article.id, + quantita, + tipo, + id_magazzino: selectedMagazzino, + causale: causale || undefined, + }), + }); - if (!res.ok) { - const data = await res.json(); - throw new Error(data.error || "Errore durante la registrazione"); + if (!res.ok) { + const data = await res.json(); + throw new Error(data.error || "Errore durante la registrazione"); + } + } else { + const res = await fetch("/api/art-macchine-parti/assegna", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + idart: article.id, + idmacchina: selectedDestinazione!.id, + quantita, + causale: causale || `Scarico verso ${selectedDestinazione!.nome}`, + }), + }); + + if (!res.ok) { + const data = await res.json(); + throw new Error(data.error || "Errore durante l'assegnazione"); + } } onSuccess(); @@ -176,46 +213,87 @@ export default function MovimentoModal({
)} - {/* Stabilimento */} -
- - -
+ {/* Sezione CARICO: Stabilimento e Magazzino */} + {isCarico && ( + <> +
+ + +
- {/* Magazzino */} - {selectedStabilimento && ( + {selectedStabilimento && ( +
+ + +
+ )} + + )} + + {/* Sezione SCARICO: Destinazione Macchina/Parte */} + {!isCarico && (
- + + {selectedDestinazione ? ( +
+
+ + {selectedDestinazione.tipo === "macchina" ? "🔧" : selectedDestinazione.tipo === "gruppo" ? "📁" : "⚙️"} + + {selectedDestinazione.nome} +
+ +
+ ) : ( + + )}
)} @@ -229,20 +307,26 @@ export default function MovimentoModal({ value={quantita} onChange={(e) => setQuantita(parseFloat(e.target.value) || 0)} min="1" + max={!isCarico ? article.quantita : undefined} className="w-full px-4 py-2 rounded-lg bg-zinc-800 border border-zinc-700 text-white focus:outline-none focus:border-blue-500 transition-colors text-lg font-semibold" /> + {!isCarico && quantita > article.quantita && ( +

+ Quantità richiesta supera la giacenza disponibile ({article.quantita}) +

+ )}
{/* Causale */}
setCausale(e.target.value)} - placeholder={isCarico ? "Es: Acquisto fornitore" : "Es: Utilizzo manutenzione"} + placeholder={isCarico ? "Es: Acquisto fornitore" : "Es: Manutenzione programmata"} className="w-full px-4 py-2 rounded-lg bg-zinc-800 border border-zinc-700 text-white placeholder-zinc-500 focus:outline-none focus:border-blue-500 transition-colors" />
@@ -258,7 +342,7 @@ export default function MovimentoModal({