feat: add side-by-side item comparison for duplicate Part Numbers

New Component: ItemComparisonModal.tsx
- Shows existing vs new item side-by-side
- Highlights fields that are different (in yellow)
- Options to Update item or Skip (local-only save)
- Shows existing item ID and comparison details

Backend Changes:
- Updated error message to say 'Part Number' not 'barcode'
- 409 response includes existing item data for comparison
- Clear, user-friendly conflict messaging

Frontend Changes:
- New state for comparison modal (newItem, existingItem, existingId)
- handleOnboardingComplete() shows modal on 409 conflict
- handleComparisonUpdate() calls updateItem() API
- handleComparisonSkip() saves locally without syncing
- Better error handling distinguishes 409 from other failures

Workflow:
1. User imports item with Part Number that already exists
2. System shows comparison modal
3. User can:
   - Update (merges new data into existing)
   - Skip (saves locally, doesn't sync to cloud)
This commit is contained in:
2026-04-17 12:35:42 +03:00
parent ade8dcde78
commit da83c91a5b
3 changed files with 187 additions and 9 deletions

View File

@@ -97,13 +97,18 @@ def create_item(
current_user: auth.TokenData = Depends(auth.get_current_user)
):
"""[C-01] Create item — only for authenticated users. [M-02] user_id from token."""
# [DUPLICATE CHECK] Prevent duplicate barcodes (part numbers)
# [DUPLICATE CHECK] Prevent duplicate part numbers
if item.barcode:
existing = db.query(models.Item).filter(models.Item.barcode == item.barcode).first()
if existing:
# Return existing item data for user comparison
raise HTTPException(
status_code=409,
detail=f"Item with barcode '{item.barcode}' already exists (ID: {existing.id}). Update it instead or use a different barcode."
detail={
"message": f"Item with Part Number '{item.barcode}' already exists in inventory.",
"existing_id": existing.id,
"existing_item": schemas.Item.model_validate(existing).model_dump()
}
)
# [AUTO-PERSIST] Create Category/Color if not exists