feat: Add batch operations, dashboard redesign, hierarchical locations
Some checks failed
CI / style (push) Has been cancelled
CI / build-client (push) Has been cancelled
CI / build-amd64 (push) Has been cancelled
CI / build-tester (push) Has been cancelled
CI / test (cockroachdb) (push) Has been cancelled
CI / test (mariadb) (push) Has been cancelled
CI / test (postgres) (push) Has been cancelled
CI / test (sqlite) (push) Has been cancelled
CI / build-arm64 (push) Has been cancelled
CI / build-armv7 (push) Has been cancelled
CI / publish-images (push) Has been cancelled
CI / publish-release (push) Has been cancelled

## Batch Operations (#13)
- Add POST /spool/bulk/archive, /bulk/delete, /bulk/update endpoints
- Add row selection with checkboxes to spool list
- Add floating BatchActionBar with archive/unarchive/delete/move actions
- Add confirmation modals for all batch operations

## Dashboard Redesign (#12)
- Install recharts for pie charts
- Add AlertCards (low stock, pending jobs, needs weighing)
- Add MaterialDistributionChart (pie chart by material)
- Add QuickStats row (total spools, filaments, vendors, weight)
- Refactor home page layout with new components

## Hierarchical Locations (#9)
- Add Location model with self-referential parent relationship
- Add migration for location table and spool.location_id FK
- Add location database operations (CRUD, tree, full path)
- Add location API endpoints (list, tree, CRUD)
- Add LocationTreeSelect component with nested tree display
- Add LocationCreateModal for inline location creation
- Keep legacy location string field for backward compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 21:40:01 -06:00
parent 27757bb949
commit 02da984b6e
18 changed files with 1683 additions and 90 deletions

View File

@@ -534,6 +534,69 @@ async def delete(
return Message(message="Success!")
# Bulk operations
class BulkArchiveParameters(BaseModel):
spool_ids: list[int] = Field(description="List of spool IDs to archive/unarchive.")
archived: bool = Field(description="Whether to archive (true) or unarchive (false).")
class BulkDeleteParameters(BaseModel):
spool_ids: list[int] = Field(description="List of spool IDs to delete.")
class BulkUpdateParameters(BaseModel):
spool_ids: list[int] = Field(description="List of spool IDs to update.")
location: Optional[str] = Field(None, max_length=64, description="New location for all spools.")
@router.post(
"/bulk/archive",
name="Bulk archive spools",
description="Archive or unarchive multiple spools at once.",
responses={404: {"model": Message}},
)
async def bulk_archive(
db: Annotated[AsyncSession, Depends(get_db_session)],
body: BulkArchiveParameters,
) -> Message:
for spool_id in body.spool_ids:
await spool.update(db=db, spool_id=spool_id, data={"archived": body.archived})
return Message(message=f"Updated {len(body.spool_ids)} spools.")
@router.post(
"/bulk/delete",
name="Bulk delete spools",
description="Delete multiple spools at once.",
responses={404: {"model": Message}},
)
async def bulk_delete(
db: Annotated[AsyncSession, Depends(get_db_session)],
body: BulkDeleteParameters,
) -> Message:
for spool_id in body.spool_ids:
await spool.delete(db=db, spool_id=spool_id)
return Message(message=f"Deleted {len(body.spool_ids)} spools.")
@router.post(
"/bulk/update",
name="Bulk update spools",
description="Update the same fields on multiple spools at once.",
responses={404: {"model": Message}},
)
async def bulk_update(
db: Annotated[AsyncSession, Depends(get_db_session)],
body: BulkUpdateParameters,
) -> Message:
data = body.model_dump(exclude={"spool_ids"}, exclude_unset=True)
for spool_id in body.spool_ids:
await spool.update(db=db, spool_id=spool_id, data=data)
return Message(message=f"Updated {len(body.spool_ids)} spools.")
@router.put(
"/{spool_id}/use",
name="Use spool filament",