Files
spoolman2/spoolman/api/v1/other.py
tonym 9bfc322f32
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
feat: Add color column with filter to spool list
Add optional color column to spool list showing filament color swatches
with multi-select filtering capability.

Backend:
- GET /api/v1/color endpoint to list distinct filament colors
- filament.color_hex query param on spool list for filtering

Frontend:
- ColorFilterColumn component renders color swatches
- useSpoolmanColors() hook fetches available colors
- Filter dropdown shows swatches with hex codes
- Column hidden by default (enable via column selector)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 21:11:18 -06:00

176 lines
4.4 KiB
Python

"""Filament related endpoints."""
import logging
from typing import Annotated
from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field, RootModel
from sqlalchemy.ext.asyncio import AsyncSession
from spoolman.database import filament, spool
from spoolman.database.database import get_db_session
logger = logging.getLogger(__name__)
router = APIRouter(
prefix="",
tags=["other"],
)
# ruff: noqa: D103, B008
@router.get(
"/material",
name="Find materials",
description="Get a list of all filament materials.",
response_model_exclude_none=True,
responses={
200: {
"description": "A list of all filament materials.",
"content": {
"application/json": {
"example": [
"PLA",
"ABS",
"PETG",
],
},
},
},
},
)
async def find_materials(
*,
db: Annotated[AsyncSession, Depends(get_db_session)],
) -> list[str]:
return await filament.find_materials(db=db)
@router.get(
"/color",
name="Find colors",
description="Get a list of all filament colors (hex codes).",
response_model_exclude_none=True,
responses={
200: {
"description": "A list of all filament color hex codes.",
"content": {
"application/json": {
"example": [
"FF0000",
"00FF00",
"0000FF",
],
},
},
},
},
)
async def find_colors(
*,
db: Annotated[AsyncSession, Depends(get_db_session)],
) -> list[str]:
return await filament.find_colors(db=db)
@router.get(
"/article-number",
name="Find article numbers",
description="Get a list of all article numbers.",
response_model_exclude_none=True,
responses={
200: {
"description": "A list of all article numbers.",
"content": {
"application/json": {
"example": [
"123456",
"987654",
],
},
},
},
},
)
async def find_article_numbers(
*,
db: Annotated[AsyncSession, Depends(get_db_session)],
) -> list[str]:
return await filament.find_article_numbers(db=db)
@router.get(
"/lot-number",
name="Find lot numbers",
description="Get a list of all lot numbers.",
response_model_exclude_none=True,
responses={
200: {
"description": "A list of all lot numbers.",
"content": {
"application/json": {
"example": [
"123456",
"987654",
],
},
},
},
},
)
async def find_lot_numbers(
*,
db: Annotated[AsyncSession, Depends(get_db_session)],
) -> list[str]:
return await spool.find_lot_numbers(db=db)
@router.get(
"/location",
name="Find locations",
description="Get a list of all spool locations.",
response_model_exclude_none=True,
responses={
200: {
"description": "A list of all spool locations.",
"content": {
"application/json": {
"example": [
"Printer 1",
"Printer 2",
"Storage Shelf A",
],
},
},
},
},
)
async def find_locations(
*,
db: Annotated[AsyncSession, Depends(get_db_session)],
) -> list[str]:
return await spool.find_locations(db=db)
class RenameLocationBody(BaseModel):
name: str = Field(description="The new name of the location.", min_length=1)
@router.patch(
"/location/{location}",
name="Rename location",
description="Rename a spool location. All spools in this location will be moved to the new location.",
response_model_exclude_none=True,
response_model=RootModel[str],
)
async def rename_location(
location: str,
*,
db: Annotated[AsyncSession, Depends(get_db_session)],
body: RenameLocationBody,
) -> str:
logger.info("Renaming location %s to %s", location, body.name)
await spool.rename_location(db=db, current_name=location, new_name=body.name)
return body.name