• Add full edit functionality for groceries with modal support
• Standardize delete confirmations across all components using ConfirmDeleteModal
• Implement complete shopping event edit functionality:
- Create EditShoppingEvent component with full form capabilities
- Add missing backend PUT endpoint for shopping events
- Support editing all event data (shop, date, groceries, amounts, prices, notes)
• Add inline grocery edit functionality in shopping event forms:
- Allow editing grocery items within add/edit forms
- Load selected items back into input fields for modification
• Fix date timezone issues in edit forms to prevent date shifting
• Remove non-functional "View Details" button in favor of working Edit button
• Enhance user experience with consistent edit/delete patterns across the app
Breaking changes: None
Backend: Added PUT /shopping-events/{id} and DELETE /shopping-events/{id} endpoints
Frontend: Complete edit workflow for all entities with improved UX
107 lines
2.5 KiB
Python
107 lines
2.5 KiB
Python
from pydantic import BaseModel, Field
|
|
from typing import Optional, List
|
|
from datetime import datetime
|
|
|
|
# Base schemas
|
|
class GroceryBase(BaseModel):
|
|
name: str
|
|
category: str
|
|
organic: bool = False
|
|
weight: Optional[float] = None
|
|
weight_unit: str = "g"
|
|
|
|
class GroceryCreate(GroceryBase):
|
|
pass
|
|
|
|
class GroceryUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
category: Optional[str] = None
|
|
organic: Optional[bool] = None
|
|
weight: Optional[float] = None
|
|
weight_unit: Optional[str] = None
|
|
|
|
class Grocery(GroceryBase):
|
|
id: int
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
# Shop schemas
|
|
class ShopBase(BaseModel):
|
|
name: str
|
|
city: str
|
|
address: Optional[str] = None
|
|
|
|
class ShopCreate(ShopBase):
|
|
pass
|
|
|
|
class ShopUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
city: Optional[str] = None
|
|
address: Optional[str] = None
|
|
|
|
class Shop(ShopBase):
|
|
id: int
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
# Shopping Event schemas
|
|
class GroceryInEvent(BaseModel):
|
|
grocery_id: int
|
|
amount: float = Field(..., gt=0)
|
|
price: float = Field(..., gt=0) # Price at the time of this shopping event
|
|
|
|
class GroceryWithEventData(BaseModel):
|
|
id: int
|
|
name: str
|
|
category: str
|
|
organic: bool
|
|
weight: Optional[float] = None
|
|
weight_unit: str
|
|
amount: float # Amount purchased in this event
|
|
price: float # Price at the time of this event
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
class ShoppingEventBase(BaseModel):
|
|
shop_id: int
|
|
date: Optional[datetime] = None
|
|
total_amount: Optional[float] = Field(None, ge=0)
|
|
notes: Optional[str] = None
|
|
|
|
class ShoppingEventCreate(ShoppingEventBase):
|
|
groceries: List[GroceryInEvent] = []
|
|
|
|
class ShoppingEventUpdate(BaseModel):
|
|
shop_id: Optional[int] = None
|
|
date: Optional[datetime] = None
|
|
total_amount: Optional[float] = Field(None, ge=0)
|
|
notes: Optional[str] = None
|
|
groceries: Optional[List[GroceryInEvent]] = None
|
|
|
|
class ShoppingEventResponse(ShoppingEventBase):
|
|
id: int
|
|
created_at: datetime
|
|
shop: Shop
|
|
groceries: List[GroceryWithEventData] = []
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
# Statistics schemas
|
|
class CategoryStats(BaseModel):
|
|
category: str
|
|
total_spent: float
|
|
item_count: int
|
|
avg_price: float
|
|
|
|
class ShopStats(BaseModel):
|
|
shop_name: str
|
|
total_spent: float
|
|
visit_count: int
|
|
avg_per_visit: float |