+ {/* Header + Selezione stabilimento */}
+
+
+
+
MagRicambi
+
Gestione magazzino ricambi
+
+ {selectedStabilimento && (
+
+
Stabilimento:
+
{selectedStabilimento.descrizione}
+
+
+ )}
+
+
+ {!selectedStabilimento && (
+
+
Seleziona uno stabilimento per iniziare:
+
+
+ )}
+
+
+ {/* Contenuto principale: split 50/50 */}
+ {selectedStabilimento ? (
+
+ {/* Colonna sinistra: Articoli */}
+
+
+ {/* Colonna destra: Macchine */}
+
+
-
) : (
-
-
-
- Nessuno stabilimento selezionato
-
-
- Seleziona uno stabilimento dalla lista sopra per visualizzare e gestire gli articoli del magazzino.
-
+
+
+
+
Seleziona uno stabilimento per visualizzare articoli e macchine
+
)}
diff --git a/src/components/ArticleCard.tsx b/src/components/ArticleCard.tsx
index 126b3c9..fa5b15f 100644
--- a/src/components/ArticleCard.tsx
+++ b/src/components/ArticleCard.tsx
@@ -10,30 +10,21 @@ interface ArticleCardProps {
}
export default function ArticleCard({ article, onEdit, onDelete }: ArticleCardProps) {
- const [showActions, setShowActions] = useState(false);
const [deleting, setDeleting] = useState(false);
const isSottoScorta = article.quantita < article.quantita_minima;
const disponibile = article.quantita - article.assegnata_macchine;
const handleDelete = async () => {
- if (!confirm(`Sei sicuro di voler eliminare l'articolo "${article.descrizione}"?`)) {
- return;
- }
+ if (!confirm(`Eliminare "${article.descrizione}"?`)) return;
setDeleting(true);
try {
- const res = await fetch(`/api/articoli/${article.id}`, {
- method: "DELETE",
- });
-
- if (!res.ok) {
- throw new Error("Errore durante l'eliminazione");
- }
-
+ const res = await fetch(`/api/articoli/${article.id}`, { method: "DELETE" });
+ if (!res.ok) throw new Error("Errore durante l'eliminazione");
onDelete(article);
} catch (error) {
- alert(error instanceof Error ? error.message : "Errore durante l'eliminazione");
+ alert(error instanceof Error ? error.message : "Errore");
} finally {
setDeleting(false);
}
@@ -42,98 +33,44 @@ export default function ArticleCard({ article, onEdit, onDelete }: ArticleCardPr
return (
setShowActions(true)}
- onMouseLeave={() => setShowActions(false)}
>
- {/* Header */}
-
-
-
-
- {article.descrizione}
-
-
{article.codice}
-
-
- {/* Badge famiglia */}
+ {/* Indicatore sotto scorta */}
+ {isSottoScorta && (
+
+ )}
+
+ {/* Info articolo */}
+
+
+
+ {article.descrizione}
+
{article.famiglia && (
-
+
{article.famiglia}
)}
-
-
- {/* Body */}
-
- {/* Quantità */}
-
-
-
Quantità
-
- {article.quantita}
- {isSottoScorta && (
-
- (min: {article.quantita_minima})
-
- )}
-
-
-
-
Disponibile
-
- {disponibile}
-
-
+
+ {article.codice}
+
+ Qta: {article.quantita}
+ {isSottoScorta && (min {article.quantita_minima})}
+
+
+ Disp: {disponibile}
+
-
- {/* Assegnate a macchine */}
- {article.assegnata_macchine > 0 && (
-
-
-
- {article.assegnata_macchine} assegnate a macchine
-
-
- )}
-
- {/* Prezzo */}
- {article.prezzo > 0 && (
-
- Prezzo
-
- €{article.prezzo.toFixed(2)}
-
-
- )}
-
- {/* Barcode */}
- {article.barcode && (
-
-
-
{article.barcode}
-
- )}
- {/* Action buttons (hover) */}
-
+ {/* Azioni */}
+
-
- {/* Sotto scorta indicator */}
- {isSottoScorta && (
-
-
- SOTTO SCORTA
-
-
- )}
);
}
diff --git a/src/components/ArticlesList.tsx b/src/components/ArticlesList.tsx
new file mode 100644
index 0000000..9d769c1
--- /dev/null
+++ b/src/components/ArticlesList.tsx
@@ -0,0 +1,148 @@
+"use client";
+
+import { useState, useEffect, useCallback } from "react";
+import type { Article, ArticleFamily } from "@/src/types/article";
+import type { Stabilimento } from "@/src/types/stabilimento";
+import ArticleCard from "./ArticleCard";
+import ArticleModal from "./ArticleModal";
+
+interface ArticlesListProps {
+ stabilimento: Stabilimento;
+}
+
+export default function ArticlesList({ stabilimento }: ArticlesListProps) {
+ const [articles, setArticles] = useState
([]);
+ const [families, setFamilies] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const [search, setSearch] = useState("");
+ const [showModal, setShowModal] = useState(false);
+ const [editingArticle, setEditingArticle] = useState(null);
+
+ const fetchArticles = useCallback(async () => {
+ setLoading(true);
+ setError(null);
+
+ try {
+ const params = new URLSearchParams();
+ params.set("stabilimentoId", stabilimento.id.toString());
+ if (search) params.set("search", search);
+
+ const res = await fetch(`/api/articoli?${params}`);
+ if (!res.ok) throw new Error("Errore nel caricamento");
+
+ const data = await res.json();
+ setArticles(data.articles);
+ setFamilies(data.families);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Errore sconosciuto");
+ } finally {
+ setLoading(false);
+ }
+ }, [stabilimento.id, search]);
+
+ useEffect(() => {
+ fetchArticles();
+ }, [fetchArticles]);
+
+ const handleEdit = (article: Article) => {
+ setEditingArticle(article);
+ setShowModal(true);
+ };
+
+ const handleDelete = (deleted: Article) => {
+ setArticles((prev) => prev.filter((a) => a.id !== deleted.id));
+ };
+
+ const handleSave = (saved: Article) => {
+ if (editingArticle) {
+ setArticles((prev) => prev.map((a) => (a.id === saved.id ? saved : a)));
+ } else {
+ setArticles((prev) => [saved, ...prev]);
+ }
+ setShowModal(false);
+ setEditingArticle(null);
+ };
+
+ const sottoScorta = articles.filter((a) => a.quantita < a.quantita_minima).length;
+
+ return (
+
+ {/* Header */}
+
+
+
Articoli
+
+ {articles.length} articoli {sottoScorta > 0 && ({sottoScorta} sotto scorta)}
+
+
+
+
+
+ {/* Ricerca */}
+
+
+
+
+
setSearch(e.target.value)}
+ placeholder="Cerca articoli..."
+ className="w-full pl-9 pr-3 py-2 text-sm rounded-lg bg-zinc-900 border border-zinc-800 text-white placeholder-zinc-500 focus:outline-none focus:border-blue-500"
+ />
+
+
+ {/* Lista articoli */}
+
+ {loading ? (
+
+
+
+
+
+ Caricamento...
+
+ ) : error ? (
+
+ ) : articles.length === 0 ? (
+
+ {search ? "Nessun articolo trovato" : "Nessun articolo in questo stabilimento"}
+
+ ) : (
+ articles.map((article) => (
+
+ ))
+ )}
+
+
+ {/* Modal */}
+ {showModal && (
+
{ setShowModal(false); setEditingArticle(null); }}
+ onSave={handleSave}
+ />
+ )}
+
+ );
+}
diff --git a/src/components/MacchineTree.tsx b/src/components/MacchineTree.tsx
new file mode 100644
index 0000000..a5018f9
--- /dev/null
+++ b/src/components/MacchineTree.tsx
@@ -0,0 +1,40 @@
+"use client";
+
+import type { Stabilimento } from "@/src/types/stabilimento";
+
+interface MacchineTreeProps {
+ stabilimento: Stabilimento;
+}
+
+export default function MacchineTree({ stabilimento }: MacchineTreeProps) {
+ return (
+
+ {/* Header */}
+
+
+
Macchine
+
Albero macchine e parti
+
+
+
+
+ {/* Placeholder contenuto */}
+
+
+
+
+
Albero macchine
+
+ Trascina gli articoli qui per assegnarli alle macchine
+
+
+
+ );
+}