From a036e33fd4686f135229d9c1689e8df2ff13a081 Mon Sep 17 00:00:00 2001 From: StefanoPutelli Date: Wed, 2 Jul 2025 22:29:39 +0200 Subject: [PATCH] added quantity used --- backend/src/models/Item.ts | 4 +- backend/src/routes/items.ts | 57 ++++++++++++---- docker-compose.yaml | 2 +- frontend/src/components/AddItemDialog.tsx | 38 ++++++++++- frontend/src/components/EditItemDialog.tsx | 4 +- .../src/components/InventoryDashboard.tsx | 30 +++++---- frontend/src/components/QuantityControls.tsx | 2 +- .../src/components/QuantityUsedControls.tsx | 65 +++++++++++++++++++ frontend/src/services/inventoryService.ts | 17 +++++ frontend/src/types/inventory.ts | 2 + 10 files changed, 189 insertions(+), 32 deletions(-) create mode 100644 frontend/src/components/QuantityUsedControls.tsx diff --git a/backend/src/models/Item.ts b/backend/src/models/Item.ts index bfd7dca..0e1e235 100644 --- a/backend/src/models/Item.ts +++ b/backend/src/models/Item.ts @@ -6,15 +6,17 @@ export interface IItem extends Document { description: string; tags: Types.Array; quantity: number; // ← nuovo + used: number; // ← nuovo, opzionale dateAdded: Date; addedBy: string; } const itemSchema = new Schema({ name: { type: String, required: true }, - description: { type: String, required: true }, + description: { type: String, required: false }, tags: [{ type: Schema.Types.ObjectId, ref: 'Tag' }], quantity: { type: Number, default: 0, min: 0 }, // default 0, mai negativo + used: { type: Number, default: 0 }, // opzionale, default false dateAdded: { type: Date, default: Date.now }, addedBy: { type: String }, }); diff --git a/backend/src/routes/items.ts b/backend/src/routes/items.ts index 820d01a..c357da1 100644 --- a/backend/src/routes/items.ts +++ b/backend/src/routes/items.ts @@ -1,6 +1,6 @@ import { Router } from 'express'; import Item from '../models/Item.js'; -import Tag from '../models/Tag.js'; +import Tag from '../models/Tag.js'; import auth from '../middleware/auth.js'; const router = Router(); @@ -18,7 +18,7 @@ router.get('/search', async (req, res) => { tagIds?: string; }; - const regex = new RegExp(query, 'i'); + const regex = new RegExp(query, 'i'); const filter: any = { $and: [ { $or: [{ name: regex }, { description: regex }] }, @@ -31,11 +31,12 @@ router.get('/search', async (req, res) => { /* ──────────── POST: crea item ──────────── */ router.post('/', auth, async (req, res) => { - const { name, description, tagIds, quantity = 0 } = req.body as { + const { name, description, tagIds, quantity = 0, used = false } = req.body as { name: string; description: string; tagIds: string[]; quantity?: number; + used?: number; }; const tags = await Tag.find({ _id: { $in: tagIds } }); @@ -44,6 +45,7 @@ router.post('/', auth, async (req, res) => { description, tags, quantity, + used, addedBy: req.user!.email, }); @@ -61,10 +63,10 @@ router.put('/:id', auth, async (req, res) => { }; const update: any = {}; - if (name !== undefined) update.name = name; + if (name !== undefined) update.name = name; if (description !== undefined) update.description = description; - if (quantity !== undefined) update.quantity = quantity; - if (tagIds) update.tags = await Tag.find({ _id: { $in: tagIds } }); + if (quantity !== undefined) update.quantity = quantity; + if (tagIds) update.tags = await Tag.find({ _id: { $in: tagIds } }); const item = await Item.findByIdAndUpdate(id, update, { new: true }).populate('tags'); if (!item) { @@ -84,17 +86,46 @@ router.patch('/:id/quantity', auth, async (req, res) => { return; } - const item = await Item.findByIdAndUpdate( - id, - { quantity }, - { new: true } - ).populate('tags'); - + const item = await Item.findById(id); if (!item) { res.status(404).json({ message: 'Item non trovato' }); return; } - res.json(item); + + if (item.used > quantity) { + res.status(400).json({ message: 'La quantità non può essere inferiore alla quantità utilizzata' }); + return; + } + + item.quantity = quantity; + await item.save(); + res.json(await item.populate('tags')); +}); + +/* ──────────── PATCH: modifica solo la quantità utilizzata ──────────── */ +router.patch('/:id/usedquantity', auth, async (req, res) => { + const { id } = req.params; + const { quantity } = req.body as { quantity: number }; + + if (quantity < 0) { + res.status(400).json({ message: 'La quantità utilizzata non può essere negativa' }); + return; + } + + const item = await Item.findById(id); + if (!item) { + res.status(404).json({ message: 'Item non trovato' }); + return; + } + + if (quantity > item.quantity) { + res.status(400).json({ message: 'La quantità utilizzata non può essere maggiore della quantità totale' }); + return; + } + + item.used = quantity; + await item.save(); + res.json(await item.populate('tags')); }); /* ──────────── DELETE: elimina item ──────────── */ diff --git a/docker-compose.yaml b/docker-compose.yaml index 9594a92..22bfed2 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -25,7 +25,7 @@ services: depends_on: - backend ports: - - "80:80" + - "2222:80" volumes: diff --git a/frontend/src/components/AddItemDialog.tsx b/frontend/src/components/AddItemDialog.tsx index eccc1ee..6d29d42 100644 --- a/frontend/src/components/AddItemDialog.tsx +++ b/frontend/src/components/AddItemDialog.tsx @@ -3,6 +3,7 @@ import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; +import { Checkbox } from './ui/checkbox'; import { Badge } from '@/components/ui/badge'; import { Dialog, @@ -28,6 +29,8 @@ export const AddItemDialog = ({ open, onOpenChange, tags, onItemAdded }: AddItem const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [selectedTagIds, setSelectedTagIds] = useState([]); + const [qt, setQt] = useState(1); // Default quantity set to 1 + const [used, setUsed] = useState(0); // Assuming this is for used tags, not implemented in the original code const [isLoading, setIsLoading] = useState(false); const { user } = useAuth(); @@ -51,7 +54,8 @@ export const AddItemDialog = ({ open, onOpenChange, tags, onItemAdded }: AddItem const newItem = await inventoryService.createItem({ name: name.trim(), description: description.trim(), - quantity: 1, // Default quantity set to 1 + quantity: qt, // Default quantity set to 1 + used: used, tagIds: selectedTagIds }); @@ -119,8 +123,8 @@ export const AddItemDialog = ({ open, onOpenChange, tags, onItemAdded }: AddItem key={tag._id} variant="outline" className={`cursor-pointer transition-all ${selectedTagIds.includes(tag._id) - ? 'bg-blue-100 border-blue-300' - : 'hover:bg-gray-100' + ? 'bg-blue-100 border-blue-300' + : 'hover:bg-gray-100' }`} onClick={() => toggleTag(tag._id)} > @@ -136,6 +140,34 @@ export const AddItemDialog = ({ open, onOpenChange, tags, onItemAdded }: AddItem Clicca sui tag per selezionarli

+
+ + { + const quantity = Math.max(1, parseInt(e.target.value, 10) || 1); + setQt(quantity); + setUsed(Math.min(used, quantity)); // Ensure used does not exceed quantity + }} + placeholder="Quantità totale" + required + /> +
+ +
+ + { + const usedQuantity = Math.max(0, parseInt(e.target.value, 10) || 0); + setUsed(usedQuantity); + setQt(Math.max(qt, usedQuantity)); // Ensure quantity is at least as much as used + }} + placeholder="Quantità usata" + /> +
+ + {item.used} + + + + ); +}; diff --git a/frontend/src/services/inventoryService.ts b/frontend/src/services/inventoryService.ts index 271839c..57f6558 100644 --- a/frontend/src/services/inventoryService.ts +++ b/frontend/src/services/inventoryService.ts @@ -80,6 +80,23 @@ export const inventoryService = { return res.json(); }, + async updateUsedItemQuantity(itemId: string, quantity: number): Promise { + const res = await fetch(`${API_URL}/items/${itemId}/usedquantity`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + ...getAuthHeader(), + }, + body: JSON.stringify({ quantity }), + }); + + if (!res.ok) { + const msg = (await res.json())?.message ?? 'Errore aggiornamento quantità usata'; + throw new Error(msg); + } + return res.json(); + }, + async deleteItem(itemId: string): Promise { const res = await fetch(`${API_URL}/items/${itemId}`, { method: 'DELETE', diff --git a/frontend/src/types/inventory.ts b/frontend/src/types/inventory.ts index 9e684c3..7403737 100644 --- a/frontend/src/types/inventory.ts +++ b/frontend/src/types/inventory.ts @@ -10,6 +10,7 @@ export interface InventoryItem { name: string; description: string; quantity: number; + used: number; tags: Tag[]; dateAdded: string; addedBy: string; @@ -19,6 +20,7 @@ export interface CreateItemData { name: string; description: string; quantity: number; + used: number; tagIds: string[]; }