feat(project): 카드 드래그 순서변경/전체멤버/탭 슬라이드/내업무 실시간반영

- 홈 프로젝트 카드 드래그 재정렬(관리자, projects.sort_order 공유)
- 프로젝트 제목 색상 pill 스타일로 강조
- 보드 세션 우클릭에 "업무 추가" 메뉴 추가
- 좌측 "멤버"→"프로젝트 멤버" + "전체 멤버" 그리드(클릭 시 담당 업무 팝업)
- 뷰 탭/스코프 토글에 슬라이드 하이라이트 애니메이션
- "내 업무" 패널이 8초 실시간 폴링에서 빠져 있던 문제 수정(홈/프로젝트 화면 모두 /api/my-tasks 로 patch)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-17 10:58:06 +09:00
parent 62ab883d24
commit 2f611767ca
7 changed files with 331 additions and 18 deletions
+48
View File
@@ -732,6 +732,22 @@ async def api_delete_project(request: Request, project_id: int) -> JSONResponse:
return JSONResponse({"ok": True})
@router.put("/api/projects/order")
async def api_reorder_projects(
request: Request, payload: dict[str, Any] = Body(...)
) -> JSONResponse:
"""홈 화면 프로젝트 카드 드래그 순서 변경. 최상위 프로젝트만 대상이며,
순서는 이 화면을 보는 모든 사용자에게 공유되므로(세션 재정렬과 동일 원칙)
관리자 전용으로 제한한다."""
_require_admin(request)
st = _db_or_503(request)
ids = payload.get("ordered_ids") or []
if not isinstance(ids, list) or not ids:
raise HTTPException(status_code=400, detail="ordered_ids 가 필요합니다.")
st.reorder_projects(ordered_ids=[int(i) for i in ids])
return JSONResponse({"ok": True})
# ────────────────────────────────────────────────────────────
# JSON API — 멤버 (관리자가 사용자 배정)
# ────────────────────────────────────────────────────────────
@@ -759,6 +775,38 @@ async def api_assignable_users(request: Request) -> JSONResponse:
return JSONResponse({"users": out})
@router.get("/api/all-users")
async def api_all_users(request: Request) -> JSONResponse:
"""프로젝트 화면 좌측 "전체 멤버" 표시용 — project 모듈 권한 보유 등록
사용자 전원. assignable-users 와 후보군은 같지만, 이건 멤버 배정(쓰기)이
아니라 단순 열람용이라 관리자가 아니어도 볼 수 있다."""
_require_user(request)
from app.main import user_store # noqa: WPS433
from app.store import has_module # noqa: WPS433
out = [
{
"email": u["email"],
"name": u.get("name") or u["email"].split("@")[0],
"id": u["email"].split("@")[0],
"avatar": u.get("picture") or "",
}
for u in user_store.list_all()
if has_module(u, "project")
]
out.sort(key=lambda x: x["id"])
return JSONResponse({"users": out})
@router.get("/api/users/tasks")
async def api_user_tasks(request: Request, email: str) -> JSONResponse:
"""'전체 멤버' 아이콘 클릭 팝업 — 지정한 사용자가 담당자인 업무 목록
(프로젝트/세션/마감일). /api/my-tasks 와 같은 조회를 이메일만 바꿔 재사용."""
_require_user(request)
st = _db_or_503(request)
return JSONResponse({"tasks": st.list_tasks(assignee_email=email, limit=500)})
@router.get("/api/projects/{project_id}/members")
async def api_list_members(request: Request, project_id: int) -> JSONResponse:
_require_user(request)