Files
playchat/backend/app/api/notes.py
T
colcaroliandClaude Opus 5 8b2231ea97 Add drag-and-drop reordering for note tabs, and a user-facing guide
Tabs can now be dragged to reorder within a group and to move between
sections in one gesture: dropping on a tab's left/right half inserts
before/after it and adopts that tab's section, while a dedicated zone
past the last group ungroups and appends at the end. A blue indicator
line shows the drop position while dragging.

core/notes.reorder() takes the full new page-id order computed
client-side (NotePanel.vue has the reactive list already) rather than
recomputing positions server-side, validates it against the current
page-id set, sets the moved page's section, then renumbers position.
POST /api/notes/reorder exposes it.

Also adds docs/notes-guide.md, an end-user walkthrough of the shared
notes panel (pages, sections, drag-and-drop, AI interaction) — the
existing docs were all developer-facing specs with no operating
instructions for the feature itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:27:38 +09:00

111 lines
3.5 KiB
Python

from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.orm import Session as DBSession
from app.core.notes import next_position, reorder, rename_section, set_section
from app.db.database import get_db
from app.db.models import NotePage
router = APIRouter(prefix="/api/notes", tags=["notes"])
class NotePageResponse(BaseModel):
id: int
title: str
content: str
position: int
section: str | None
updated_at: datetime
class Config:
from_attributes = True
class NotePageCreate(BaseModel):
title: str = "新しいページ"
content: str = ""
section: str | None = None
class NotePageUpdate(BaseModel):
title: str | None = None
content: str | None = None
section: str | None = None
class SectionRenameRequest(BaseModel):
old_name: str
new_name: str | None = None
class ReorderRequest(BaseModel):
page_id: int
section: str | None = None
order: list[int]
@router.get("", response_model=list[NotePageResponse])
def list_pages(db: DBSession = Depends(get_db)):
return db.query(NotePage).order_by(NotePage.position, NotePage.id).all()
@router.post("", response_model=NotePageResponse, status_code=201)
def create_page(req: NotePageCreate, db: DBSession = Depends(get_db)):
title = req.title.strip() or "新しいページ"
page = NotePage(title=title, content=req.content, position=next_position(db))
db.add(page)
db.commit()
db.refresh(page)
if req.section is not None and req.section.strip():
page = set_section(db, page.id, req.section)
return page
@router.post("/sections/rename")
def rename_section_endpoint(req: SectionRenameRequest, db: DBSession = Depends(get_db)):
"""old_name セクションに属する全ページを new_name セクションへ一括で付け替える。new_name が空ならグループ解除。"""
count = rename_section(db, req.old_name, req.new_name)
return {"renamed": count}
@router.post("/reorder")
def reorder_pages(req: ReorderRequest, db: DBSession = Depends(get_db)):
"""タブのドラッグ&ドロップによる並べ替え。order は全ページIDを新しい順序で並べたもの。"""
ok = reorder(db, req.page_id, req.section, req.order)
if not ok:
raise HTTPException(status_code=400, detail="order must contain exactly the current page ids")
return {"reordered": len(req.order)}
@router.patch("/{page_id}", response_model=NotePageResponse)
def update_page(page_id: int, req: NotePageUpdate, db: DBSession = Depends(get_db)):
page = db.get(NotePage, page_id)
if not page:
raise HTTPException(status_code=404, detail="Note page not found")
if req.title is not None:
title = req.title.strip()
if not title:
raise HTTPException(status_code=400, detail="title is empty")
page.title = title
if req.content is not None:
page.content = req.content
db.commit()
db.refresh(page)
# section はページの並び順にも影響するため、変更があれば専用ロジックで反映する
if "section" in req.model_fields_set:
page = set_section(db, page_id, req.section)
return page
@router.delete("/{page_id}", status_code=204)
def delete_page(page_id: int, db: DBSession = Depends(get_db)):
page = db.get(NotePage, page_id)
if not page:
raise HTTPException(status_code=404, detail="Note page not found")
db.delete(page)
db.commit()