feat(shopping): listes projet + déduplication nommage hebdo
Backend : - Migration 007 : list_type VARCHAR(20) sur shopping.lists (weekly/project), url/description/image_url sur shopping.list_items - Modèle ShoppingList : champ list_type - Modèle ListItem : champs url, description, image_url - Schémas : list_type sur Create/Response, nouveaux champs sur ItemCreate/Update/Response - _unique_week_label() : évite les doublons S22 2026 → S22 2026 (2) - finish_shopping : carry-over uniquement pour list_type='weekly' Frontend : - api/shopping.ts : list_type, champs enrichis item, createProjectList() - ProjectItemCard.tsx : carte avec image, description, URL, boutique, cochage - ShoppingPage : · Séparation weekly / project dans la sélection de liste active · Section "Listes projet" sur l'écran vide avec navigation · Badge PROJET dans l'en-tête · Bouton "Clôturer la semaine" et badge "semaine dépassée" masqués sur projet · Bouton "+ Ajouter" (mobile + laptop) sur les listes projet · Vue grille ProjectItemCard pour les listes projet · Modale création liste projet (nom + boutique) · Modale ajout/édition item projet (nom, description, URL, image URL) v0.5.14 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -83,6 +83,9 @@ class ListItemCreate(BaseModel):
|
||||
custom_name: str | None = None
|
||||
quantity: Decimal | None = None
|
||||
unit: str | None = None
|
||||
url: str | None = None
|
||||
description: str | None = None
|
||||
image_url: str | None = None
|
||||
|
||||
@model_validator(mode='after')
|
||||
def must_have_name(self) -> 'ListItemCreate':
|
||||
@@ -96,6 +99,9 @@ class ListItemUpdate(BaseModel):
|
||||
quantity: Decimal | None = None
|
||||
unit: str | None = None
|
||||
price_recorded: Decimal | None = None
|
||||
url: str | None = None
|
||||
description: str | None = None
|
||||
image_url: str | None = None
|
||||
|
||||
|
||||
class ListItemResponse(BaseModel):
|
||||
@@ -110,13 +116,23 @@ class ListItemResponse(BaseModel):
|
||||
price_recorded: Decimal | None
|
||||
carried_over: bool
|
||||
sort_order: int | None
|
||||
url: str | None
|
||||
description: str | None
|
||||
image_url: str | None
|
||||
|
||||
|
||||
class ShoppingListCreate(BaseModel):
|
||||
name: str | None = None
|
||||
list_type: Literal['weekly', 'project'] = 'weekly'
|
||||
store_id: uuid.UUID | None = None
|
||||
week_date: date | None = None
|
||||
|
||||
@model_validator(mode='after')
|
||||
def project_requires_name(self) -> 'ShoppingListCreate':
|
||||
if self.list_type == 'project' and not self.name:
|
||||
raise ValueError('Une liste projet doit avoir un nom')
|
||||
return self
|
||||
|
||||
|
||||
class ShoppingListUpdate(BaseModel):
|
||||
name: str | None = None
|
||||
@@ -128,6 +144,7 @@ class ShoppingListResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
id: uuid.UUID
|
||||
name: str | None
|
||||
list_type: str
|
||||
store_id: uuid.UUID | None
|
||||
week_date: date | None
|
||||
status: str
|
||||
@@ -140,6 +157,7 @@ class ShoppingListDetailResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
id: uuid.UUID
|
||||
name: str | None
|
||||
list_type: str
|
||||
store_id: uuid.UUID | None
|
||||
week_date: date | None
|
||||
status: str
|
||||
|
||||
Reference in New Issue
Block a user