Add category management UI and service layer
Users can now create, edit, and delete custom categories from Settings → Categories. System categories (45 built-in) are shown read-only. Backend adds update_category() and delete_category() service functions; frontend has a new categories API module and a full CRUD section in SettingsPage with filter tabs, colour picker, and delete confirmation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8ef3bb2965
commit
6111424f47
5 changed files with 455 additions and 22 deletions
|
|
@ -1,14 +1,28 @@
|
|||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.dependencies import get_current_user, get_db
|
||||
from app.services.category_service import create_category, list_categories
|
||||
from app.services.category_service import create_category, list_categories, update_category, delete_category
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class CategoryCreate(BaseModel):
|
||||
name: str = Field(..., min_length=1, max_length=100)
|
||||
type: str = Field(..., pattern="^(income|expense|transfer)$")
|
||||
color: str | None = Field(default=None, max_length=7)
|
||||
icon: str | None = Field(default=None, max_length=50)
|
||||
|
||||
|
||||
class CategoryUpdate(BaseModel):
|
||||
name: str | None = Field(default=None, min_length=1, max_length=100)
|
||||
color: str | None = None
|
||||
icon: str | None = None
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def get_categories(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
|
|
@ -19,18 +33,43 @@ async def get_categories(
|
|||
|
||||
@router.post("", status_code=201)
|
||||
async def create(
|
||||
body: dict,
|
||||
body: CategoryCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user=Depends(get_current_user),
|
||||
):
|
||||
result = await create_category(
|
||||
db,
|
||||
user_id=user.id,
|
||||
name=body["name"],
|
||||
type_=body["type"],
|
||||
icon=body.get("icon"),
|
||||
color=body.get("color"),
|
||||
parent_id=uuid.UUID(body["parent_id"]) if body.get("parent_id") else None,
|
||||
name=body.name,
|
||||
type_=body.type,
|
||||
icon=body.icon,
|
||||
color=body.color,
|
||||
)
|
||||
await db.commit()
|
||||
return result
|
||||
|
||||
|
||||
@router.put("/{category_id}", status_code=200)
|
||||
async def update(
|
||||
category_id: uuid.UUID,
|
||||
body: CategoryUpdate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user=Depends(get_current_user),
|
||||
):
|
||||
result = await update_category(db, user.id, category_id, body.name, body.color, body.icon)
|
||||
if result is None:
|
||||
raise HTTPException(status_code=404, detail="Category not found or is a system category")
|
||||
await db.commit()
|
||||
return result
|
||||
|
||||
|
||||
@router.delete("/{category_id}", status_code=204)
|
||||
async def delete(
|
||||
category_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user=Depends(get_current_user),
|
||||
):
|
||||
ok = await delete_category(db, user.id, category_id)
|
||||
if not ok:
|
||||
raise HTTPException(status_code=404, detail="Category not found or is a system category")
|
||||
await db.commit()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue