diff --git a/src/app/(management)/stabilimenti/page.tsx b/src/app/(management)/stabilimenti/page.tsx new file mode 100644 index 0000000..4afb86e --- /dev/null +++ b/src/app/(management)/stabilimenti/page.tsx @@ -0,0 +1,11 @@ +"use client"; + +import StabilimentiManager from "@/src/components/StabilimentiManager"; + +export default function StabilimentiPage() { + return ( +
+ +
+ ); +} diff --git a/src/app/api/magazzini/[id]/route.ts b/src/app/api/magazzini/[id]/route.ts new file mode 100644 index 0000000..d473712 --- /dev/null +++ b/src/app/api/magazzini/[id]/route.ts @@ -0,0 +1,79 @@ +import { NextResponse } from "next/server"; + +import { auth } from "@/src/auth"; +import { getMagazzinoById, updateMagazzino, deleteMagazzino } from "@/src/lib/stabilimenti"; + +interface RouteContext { + params: Promise<{ id: string }>; +} + +async function readId(params: Promise<{ id: string }>): Promise { + const { id } = await params; + const magazzinoId = Number(id); + + if (!Number.isInteger(magazzinoId) || magazzinoId <= 0) { + throw new Error("ID magazzino non valido."); + } + + return magazzinoId; +} + +export async function GET(_request: Request, context: RouteContext) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + const magazzino = await getMagazzinoById(await readId(context.params)); + if (!magazzino) { + return NextResponse.json({ error: "Magazzino non trovato" }, { status: 404 }); + } + return NextResponse.json({ magazzino }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Errore recupero magazzino" }, + { status: 400 } + ); + } +} + +export async function PUT(request: Request, context: RouteContext) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + const magazzino = await updateMagazzino( + await readId(context.params), + await request.json() + ); + return NextResponse.json({ magazzino }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Errore modifica magazzino" }, + { status: 400 } + ); + } +} + +export async function DELETE(_request: Request, context: RouteContext) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + await deleteMagazzino(await readId(context.params)); + return NextResponse.json({ ok: true }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Errore eliminazione magazzino" }, + { status: 400 } + ); + } +} diff --git a/src/app/api/magazzini/route.ts b/src/app/api/magazzini/route.ts index dc04d3c..599e6be 100644 --- a/src/app/api/magazzini/route.ts +++ b/src/app/api/magazzini/route.ts @@ -1,7 +1,7 @@ import { NextResponse } from "next/server"; import { auth } from "@/src/auth"; -import { listMagazzini } from "@/src/lib/stabilimenti"; +import { listMagazzini, createMagazzino } from "@/src/lib/stabilimenti"; export async function GET(request: Request) { const session = await auth(); @@ -27,3 +27,21 @@ export async function GET(request: Request) { ); } } + +export async function POST(request: Request) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + const magazzino = await createMagazzino(await request.json()); + return NextResponse.json({ magazzino }, { status: 201 }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Errore creazione magazzino" }, + { status: 400 } + ); + } +} diff --git a/src/app/api/stabilimenti/[id]/route.ts b/src/app/api/stabilimenti/[id]/route.ts new file mode 100644 index 0000000..3bff566 --- /dev/null +++ b/src/app/api/stabilimenti/[id]/route.ts @@ -0,0 +1,83 @@ +import { NextResponse } from "next/server"; + +import { auth } from "@/src/auth"; +import { + getStabilimentoById, + updateStabilimento, + deleteStabilimento, +} from "@/src/lib/stabilimenti"; + +interface RouteContext { + params: Promise<{ id: string }>; +} + +async function readId(params: Promise<{ id: string }>): Promise { + const { id } = await params; + const stabilimentoId = Number(id); + + if (!Number.isInteger(stabilimentoId) || stabilimentoId <= 0) { + throw new Error("ID stabilimento non valido."); + } + + return stabilimentoId; +} + +export async function GET(_request: Request, context: RouteContext) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + const stabilimento = await getStabilimentoById(await readId(context.params)); + if (!stabilimento) { + return NextResponse.json({ error: "Stabilimento non trovato" }, { status: 404 }); + } + return NextResponse.json({ stabilimento }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Errore recupero stabilimento" }, + { status: 400 } + ); + } +} + +export async function PUT(request: Request, context: RouteContext) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + const stabilimento = await updateStabilimento( + await readId(context.params), + await request.json() + ); + return NextResponse.json({ stabilimento }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Errore modifica stabilimento" }, + { status: 400 } + ); + } +} + +export async function DELETE(_request: Request, context: RouteContext) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + await deleteStabilimento(await readId(context.params)); + return NextResponse.json({ ok: true }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Errore eliminazione stabilimento" }, + { status: 400 } + ); + } +} diff --git a/src/app/api/stabilimenti/route.ts b/src/app/api/stabilimenti/route.ts index 2fd9dac..926f05d 100644 --- a/src/app/api/stabilimenti/route.ts +++ b/src/app/api/stabilimenti/route.ts @@ -1,7 +1,11 @@ import { NextResponse } from "next/server"; import { auth } from "@/src/auth"; -import { listStabilimenti, getStabilimentiWithMagazzini } from "@/src/lib/stabilimenti"; +import { + listStabilimenti, + getStabilimentiWithMagazzini, + createStabilimento, +} from "@/src/lib/stabilimenti"; export async function GET(request: Request) { const session = await auth(); @@ -29,3 +33,21 @@ export async function GET(request: Request) { ); } } + +export async function POST(request: Request) { + const session = await auth(); + + if (!session?.user) { + return NextResponse.json({ error: "Non autorizzato" }, { status: 401 }); + } + + try { + const stabilimento = await createStabilimento(await request.json()); + return NextResponse.json({ stabilimento }, { status: 201 }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Errore creazione stabilimento" }, + { status: 400 } + ); + } +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index ec7bc38..6da4e0e 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -20,6 +20,7 @@ const navLinks: NavLink[] = [ { href: "/movimenti", label: "Movimenti"}, { href: "/articoli", label: "Articoli"}, { href: "/macchine", label: "Macchine"}, + { href: "/stabilimenti", label: "Stabilimenti"}, { href: "/utenti", label: "Utenti"}, ]; diff --git a/src/components/StabilimentiManager.tsx b/src/components/StabilimentiManager.tsx new file mode 100644 index 0000000..3b997ae --- /dev/null +++ b/src/components/StabilimentiManager.tsx @@ -0,0 +1,431 @@ +"use client"; + +import { useState, useEffect, useCallback } from "react"; +import type { Stabilimento, Magazzino, StabilimentoWithMagazzini } from "@/src/types/stabilimento"; +import { useStabilimento } from "@/src/contexts/StabilimentoContext"; + +function FormModal({ + title, + onClose, + onSubmit, + children, + submitLabel, +}: { + title: string; + onClose: () => void; + onSubmit: (e: React.FormEvent) => void; + children: React.ReactNode; + submitLabel: string; +}) { + return ( +
+
+
+

{title}

+
+ {children} +
+ + +
+
+
+
+ ); +} + +export default function StabilimentiManager() { + const { reload } = useStabilimento(); + const [stabilimenti, setStabilimenti] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [search, setSearch] = useState(""); + + const [stabModal, setStabModal] = useState<"create" | "edit" | null>(null); + const [magModal, setMagModal] = useState<"create" | "edit" | null>(null); + const [editingStabilimento, setEditingStabilimento] = useState(null); + const [editingMagazzino, setEditingMagazzino] = useState(null); + const [presetStabilimentoId, setPresetStabilimentoId] = useState(null); + + const [stabDescrizione, setStabDescrizione] = useState(""); + const [magDescrizione, setMagDescrizione] = useState(""); + const [magStabilimentoId, setMagStabilimentoId] = useState(""); + + const loadData = useCallback(async () => { + setLoading(true); + setError(null); + try { + const res = await fetch("/api/stabilimenti?includeMagazzini=true"); + if (!res.ok) throw new Error("Errore nel caricamento"); + const data = await res.json(); + setStabilimenti(data.stabilimenti || []); + } catch (err) { + setError(err instanceof Error ? err.message : "Errore sconosciuto"); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + loadData(); + }, [loadData]); + + const afterMutation = async () => { + await loadData(); + await reload(); + }; + + const openCreateStabilimento = () => { + setEditingStabilimento(null); + setStabDescrizione(""); + setStabModal("create"); + }; + + const openEditStabilimento = (stab: Stabilimento) => { + setEditingStabilimento(stab); + setStabDescrizione(stab.descrizione); + setStabModal("edit"); + }; + + const openCreateMagazzino = (stabilimentoId?: number) => { + setEditingMagazzino(null); + setMagDescrizione(""); + setMagStabilimentoId(stabilimentoId?.toString() || ""); + setPresetStabilimentoId(stabilimentoId ?? null); + setMagModal("create"); + }; + + const openEditMagazzino = (mag: Magazzino) => { + setEditingMagazzino(mag); + setMagDescrizione(mag.descrizione); + setMagStabilimentoId(mag.id_stabilimento.toString()); + setMagModal("edit"); + }; + + const handleStabilimentoSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + try { + const url = + stabModal === "edit" && editingStabilimento + ? `/api/stabilimenti/${editingStabilimento.id}` + : "/api/stabilimenti"; + const method = stabModal === "edit" ? "PUT" : "POST"; + const res = await fetch(url, { + method, + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ descrizione: stabDescrizione }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || "Operazione fallita"); + setStabModal(null); + await afterMutation(); + } catch (err) { + setError(err instanceof Error ? err.message : "Errore"); + } + }; + + const handleMagazzinoSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + try { + const url = + magModal === "edit" && editingMagazzino + ? `/api/magazzini/${editingMagazzino.id}` + : "/api/magazzini"; + const method = magModal === "edit" ? "PUT" : "POST"; + const res = await fetch(url, { + method, + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + descrizione: magDescrizione, + id_stabilimento: parseInt(magStabilimentoId, 10), + }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || "Operazione fallita"); + setMagModal(null); + await afterMutation(); + } catch (err) { + setError(err instanceof Error ? err.message : "Errore"); + } + }; + + const handleDeleteStabilimento = async (id: number, nome: string) => { + if (!confirm(`Eliminare lo stabilimento "${nome}"?`)) return; + setError(null); + try { + const res = await fetch(`/api/stabilimenti/${id}`, { method: "DELETE" }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || "Eliminazione fallita"); + await afterMutation(); + } catch (err) { + setError(err instanceof Error ? err.message : "Errore"); + } + }; + + const handleDeleteMagazzino = async (id: number, nome: string) => { + if (!confirm(`Eliminare il magazzino "${nome}"?`)) return; + setError(null); + try { + const res = await fetch(`/api/magazzini/${id}`, { method: "DELETE" }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || "Eliminazione fallita"); + await afterMutation(); + } catch (err) { + setError(err instanceof Error ? err.message : "Errore"); + } + }; + + const filtered = stabilimenti.filter((s) => + s.descrizione.toLowerCase().includes(search.toLowerCase()) + ); + + const totalMagazzini = stabilimenti.reduce((n, s) => n + s.magazzini.length, 0); + + return ( +
+
+

Stabilimenti e Magazzini

+

+ Anagrafica sedi produttive e magazzini associati +

+
+ +
+
+

Stabilimenti

+

{stabilimenti.length}

+
+
+

Magazzini totali

+

{totalMagazzini}

+
+
+ + {error && ( +
+ {error} + +
+ )} + +
+ setSearch(e.target.value)} + placeholder="Cerca stabilimenti..." + className="w-full md:max-w-md px-4 py-2 rounded-lg bg-zinc-900 border border-zinc-800 text-white placeholder-zinc-500 focus:outline-none focus:border-blue-500" + /> +
+ + +
+
+ + {loading ? ( +
+ + + + + Caricamento... +
+ ) : filtered.length === 0 ? ( +
+

Nessuno stabilimento

+

+ {search ? "Nessun risultato per la ricerca" : "Aggiungi il primo stabilimento"} +

+ {!search && ( + + )} +
+ ) : ( +
+ {filtered.map((stab) => ( +
+
+
+ 🏭 +
+

{stab.descrizione}

+

+ {stab.magazzini.length} magazzin{stab.magazzini.length === 1 ? "o" : "i"} +

+
+
+
+ + + +
+
+ +
+ {stab.magazzini.length === 0 ? ( +

Nessun magazzino associato

+ ) : ( +
    + {stab.magazzini.map((mag) => ( +
  • + + 📦 + {mag.descrizione} + +
    + + +
    +
  • + ))} +
+ )} +
+
+ ))} +
+ )} + + {stabModal && ( + setStabModal(null)} + onSubmit={handleStabilimentoSubmit} + submitLabel={stabModal === "edit" ? "Salva" : "Crea"} + > +
+ + setStabDescrizione(e.target.value)} + required + className="w-full px-3 py-2 rounded-lg bg-zinc-950 border border-zinc-700 text-white focus:outline-none focus:border-blue-500" + placeholder="Es. Stabilimento Gonzaga" + /> +
+
+ )} + + {magModal && ( + setMagModal(null)} + onSubmit={handleMagazzinoSubmit} + submitLabel={magModal === "edit" ? "Salva" : "Crea"} + > +
+ + +
+
+ + setMagDescrizione(e.target.value)} + required + className="w-full px-3 py-2 rounded-lg bg-zinc-950 border border-zinc-700 text-white focus:outline-none focus:border-blue-500" + placeholder="Es. Magazzino ricambi" + /> +
+
+ )} +
+ ); +} diff --git a/src/contexts/StabilimentoContext.tsx b/src/contexts/StabilimentoContext.tsx index 32719ff..b5de2cb 100644 --- a/src/contexts/StabilimentoContext.tsx +++ b/src/contexts/StabilimentoContext.tsx @@ -20,6 +20,7 @@ interface StabilimentoContextValue { setSelectedMagazzino: (magazzino: Magazzino | null) => void; filteredMagazzini: Magazzino[]; resetSelection: () => void; + reload: () => Promise; } const StabilimentoContext = createContext(null); @@ -31,32 +32,48 @@ export function StabilimentoProvider({ children }: { children: ReactNode }) { const [selectedMagazzino, setSelectedMagazzinoState] = useState(null); const [loading, setLoading] = useState(true); + const reload = useCallback(async () => { + try { + const [stabRes, magRes] = await Promise.all([ + fetch("/api/stabilimenti"), + fetch("/api/magazzini"), + ]); + + if (stabRes.ok) { + const stabData = await stabRes.json(); + const list: Stabilimento[] = stabData.stabilimenti || []; + setStabilimenti(list); + + setSelectedStabilimentoState((prev) => { + if (!prev) return prev; + return list.find((s) => s.id === prev.id) ?? null; + }); + } + + if (magRes.ok) { + const magData = await magRes.json(); + const list: Magazzino[] = magData.magazzini || []; + setMagazzini(list); + + setSelectedMagazzinoState((prev) => { + if (!prev) return prev; + return list.find((m) => m.id === prev.id) ?? null; + }); + } + } catch (error) { + console.error("Errore caricamento stabilimenti/magazzini:", error); + } + }, []); + useEffect(() => { async function fetchData() { - try { - const [stabRes, magRes] = await Promise.all([ - fetch("/api/stabilimenti"), - fetch("/api/magazzini"), - ]); - - if (stabRes.ok) { - const stabData = await stabRes.json(); - setStabilimenti(stabData.stabilimenti || []); - } - - if (magRes.ok) { - const magData = await magRes.json(); - setMagazzini(magData.magazzini || []); - } - } catch (error) { - console.error("Errore caricamento stabilimenti/magazzini:", error); - } finally { - setLoading(false); - } + setLoading(true); + await reload(); + setLoading(false); } fetchData(); - }, []); + }, [reload]); const setSelectedStabilimento = useCallback((stabilimento: Stabilimento | null) => { setSelectedStabilimentoState(stabilimento); @@ -88,6 +105,7 @@ export function StabilimentoProvider({ children }: { children: ReactNode }) { setSelectedMagazzino, filteredMagazzini, resetSelection, + reload, }} > {children} diff --git a/src/lib/stabilimenti/index.ts b/src/lib/stabilimenti/index.ts index 405a456..5a86e09 100644 --- a/src/lib/stabilimenti/index.ts +++ b/src/lib/stabilimenti/index.ts @@ -1,7 +1,13 @@ -import type { RowDataPacket } from "mysql2"; +import type { ResultSetHeader, RowDataPacket } from "mysql2"; import { db } from "@/src/lib/db"; -import type { Stabilimento, Magazzino, StabilimentoWithMagazzini } from "@/src/types/stabilimento"; +import type { + Stabilimento, + Magazzino, + StabilimentoWithMagazzini, + StabilimentoInput, + MagazzinoInput, +} from "@/src/types/stabilimento"; type StabilimentoRow = Stabilimento & RowDataPacket; type MagazzinoRow = Magazzino & RowDataPacket; @@ -87,3 +93,191 @@ export async function getMagazzinoById(id: number): Promise { stabilimento: row.stabilimento, }; } + +function cleanText(value: unknown): string { + return String(value ?? "").trim(); +} + +function parseStabilimentoInput(input: Partial): StabilimentoInput { + const descrizione = cleanText(input.descrizione); + if (!descrizione) { + throw new Error("La descrizione dello stabilimento è obbligatoria."); + } + return { descrizione }; +} + +function parseMagazzinoInput(input: Partial): MagazzinoInput { + const descrizione = cleanText(input.descrizione); + const id_stabilimento = Number(input.id_stabilimento); + + if (!descrizione) { + throw new Error("La descrizione del magazzino è obbligatoria."); + } + if (!Number.isInteger(id_stabilimento) || id_stabilimento <= 0) { + throw new Error("Seleziona uno stabilimento valido per il magazzino."); + } + + return { descrizione, id_stabilimento }; +} + +export async function createStabilimento(input: Partial): Promise { + const { descrizione } = parseStabilimentoInput(input); + + const [result] = await db.execute( + "INSERT INTO stabilimenti (descrizione) VALUES (:descrizione)", + { descrizione } + ); + + const created = await getStabilimentoById(result.insertId); + if (!created) { + throw new Error("Errore nella creazione dello stabilimento."); + } + return created; +} + +export async function updateStabilimento( + id: number, + input: Partial +): Promise { + const existing = await getStabilimentoById(id); + if (!existing) { + throw new Error("Stabilimento non trovato."); + } + + const { descrizione } = parseStabilimentoInput(input); + + await db.execute( + "UPDATE stabilimenti SET descrizione = :descrizione WHERE id = :id", + { descrizione, id } + ); + + const updated = await getStabilimentoById(id); + if (!updated) { + throw new Error("Errore nell'aggiornamento dello stabilimento."); + } + return updated; +} + +export async function deleteStabilimento(id: number): Promise { + const existing = await getStabilimentoById(id); + if (!existing) { + throw new Error("Stabilimento non trovato."); + } + + const [magRows] = await db.execute( + "SELECT COUNT(*) AS cnt FROM magazzini WHERE id_stabilimento = :id", + { id } + ); + if (Number(magRows[0]?.cnt) > 0) { + throw new Error( + "Impossibile eliminare: esistono magazzini associati. Elimina prima i magazzini." + ); + } + + const [macRows] = await db.execute( + "SELECT COUNT(*) AS cnt FROM macchine_parti WHERE id_stabilimento = :id", + { id } + ); + if (Number(macRows[0]?.cnt) > 0) { + throw new Error( + "Impossibile eliminare: esistono macchine associate a questo stabilimento." + ); + } + + await db.execute("DELETE FROM stabilimenti WHERE id = :id", { id }); +} + +export async function createMagazzino(input: Partial): Promise { + const parsed = parseMagazzinoInput(input); + + const stabilimento = await getStabilimentoById(parsed.id_stabilimento); + if (!stabilimento) { + throw new Error("Stabilimento non trovato."); + } + + const [result] = await db.execute( + "INSERT INTO magazzini (descrizione, id_stabilimento) VALUES (:descrizione, :id_stabilimento)", + { + descrizione: parsed.descrizione, + id_stabilimento: parsed.id_stabilimento, + } + ); + + const created = await getMagazzinoById(result.insertId); + if (!created) { + throw new Error("Errore nella creazione del magazzino."); + } + return created; +} + +export async function updateMagazzino( + id: number, + input: Partial +): Promise { + const existing = await getMagazzinoById(id); + if (!existing) { + throw new Error("Magazzino non trovato."); + } + + const parsed = parseMagazzinoInput(input); + + const stabilimento = await getStabilimentoById(parsed.id_stabilimento); + if (!stabilimento) { + throw new Error("Stabilimento non trovato."); + } + + await db.execute( + "UPDATE magazzini SET descrizione = :descrizione, id_stabilimento = :id_stabilimento WHERE id = :id", + { + descrizione: parsed.descrizione, + id_stabilimento: parsed.id_stabilimento, + id, + } + ); + + const updated = await getMagazzinoById(id); + if (!updated) { + throw new Error("Errore nell'aggiornamento del magazzino."); + } + return updated; +} + +export async function deleteMagazzino(id: number): Promise { + const existing = await getMagazzinoById(id); + if (!existing) { + throw new Error("Magazzino non trovato."); + } + + const [giacRows] = await db.execute( + "SELECT COUNT(*) AS cnt FROM giacenze WHERE id_magazzino = :id", + { id } + ); + if (Number(giacRows[0]?.cnt) > 0) { + throw new Error( + "Impossibile eliminare: il magazzino contiene giacenze di articoli." + ); + } + + const [movRows] = await db.execute( + `SELECT COUNT(*) AS cnt FROM movimenti + WHERE id_magazzino_origine = :id OR id_magazzino_destinazione = :id`, + { id } + ); + if (Number(movRows[0]?.cnt) > 0) { + throw new Error( + "Impossibile eliminare: il magazzino è referenziato in movimenti di magazzino." + ); + } + + const [artRows] = await db.execute( + "SELECT COUNT(*) AS cnt FROM articoli WHERE id_magazzino = :id", + { id } + ); + if (Number(artRows[0]?.cnt) > 0) { + throw new Error( + "Impossibile eliminare: articoli del catalogo sono collegati a questo magazzino." + ); + } + + await db.execute("DELETE FROM magazzini WHERE id = :id", { id }); +} diff --git a/src/types/stabilimento.ts b/src/types/stabilimento.ts index d812be8..6312e43 100644 --- a/src/types/stabilimento.ts +++ b/src/types/stabilimento.ts @@ -13,3 +13,12 @@ export interface Magazzino { export interface StabilimentoWithMagazzini extends Stabilimento { magazzini: Magazzino[]; } + +export interface StabilimentoInput { + descrizione: string; +} + +export interface MagazzinoInput { + descrizione: string; + id_stabilimento: number; +}