[feat/MAT-685] 집중학습 카드 어드민 + 발행 출제근거 매핑#328
Conversation
- Regen OpenAPI schema from QA exposing new focus-card and publish-focus-card-link endpoints - focusCard/* controllers for template CRUD, per-student issuance, candidates lookup - publish/* controllers for focus-card-link CRUD and bulk candidates query (POST-as-read via useQuery + body) - analytics/getConceptHistory for student vulnerability lookup - Extend conceptGraph/getNode with optional onlyFocusCardCandidates param (default false, backwards-compatible) - Add 3 param interfaces to types/api/queryParams.ts Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- /focus-card: template list + delete - /focus-card/register: new template with FocusCardActionNodePicker (uses onlyFocusCardCandidates to hide nodes that already have a card) - /focus-card/$focusCardId: template detail/content edit with ActionNode description / payload (example, pointingExample) panel - /focus-card/issuance: per-student issuance management — by-date list, manual issue modal, revoke, auto-issue trigger, student vulnerability panel (top-5 weak concepts via analytics) - /publish/$publishId: publish detail with focus card link chips per problem + add-modal that loads only problem-matching candidates - /publish (modify): Day cell hover Eye button → publish detail - /publish/register/$publishDate/$studentId (modify): inline focus card mapping section using bulk candidates query (POST /publish/focus-card-link-candidates); only matching cards selectable; submit body includes focusCardLinks - Add useActionNodeDetail / useActionNodeTypeId hooks - Add 4 invalidators: focus card list/item, issuance by-date, publish detail - Add FOCUS_CARD / FOCUS_CARD_ISSUANCE menus under new 집중학습 nav section (between 문제 관리 and 개념 그래프). SUPER admin only until BE menu seed lands - Refactor canAccessPath to longest-prefix-wins (getMostSpecificNavItem) so /focus-card permission does not leak into /focus-card/issuance; GNB active state follows the same rule Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 62ca4f6ee1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const problemSetId = publish?.problemSet?.id ?? 0; | ||
| const { data: problemSet } = getProblemSetById({ id: problemSetId }); |
There was a problem hiding this comment.
Defer problem-set fetch until publish data is available
getProblemSetById is invoked with problemSetId defaulting to 0 before getPublishById resolves, so this page sends an unnecessary /api/admin/problem-set/0 request on initial render. That call is guaranteed to fail for normal data, adds avoidable network/error noise, and can trigger retries before the real problem-set id is known. Gate this query with an enabled check (or conditional call) until publish?.problemSet?.id is present.
Useful? React with 👍 / 👎.
| onSuccess: () => { | ||
| invalidateFocusCardIssuanceByDate(studentId, issuedDate); | ||
| toast.success('카드가 발급되었습니다'); |
There was a problem hiding this comment.
Invalidate publish-link candidates after issuance mutations
After issuing/revoking cards, this code only invalidates issuance/by-date; it never invalidates the candidate query used by /publish/register (/api/admin/publish/focus-card-link-candidates). Because queries are configured as perpetually fresh (staleTime: Infinity), previously viewed candidate results remain cached and newly issued cards may not appear in mapping UI until a hard reload. Add candidate-query invalidation (or force refetch semantics) in these mutation success handlers.
Useful? React with 👍 / 👎.
Summary
GNB
admin_menu시드 마이그레이션 follow-up 필요 (FOCUS_CARD,FOCUS_CARD_ISSUANCE).canAccessPath를 longest-prefix-wins (getMostSpecificNavItem) 로 리팩터 —/focus-card권한이/focus-card/issuance까지 흡수하던 누수 제거.GNBactive state 도 동일 규칙으로 통일 (TanStackLink기본 prefix 매칭으로 중복 highlight 되던 회귀 fix).신규 페이지
/focus-card): 카드 그리드 + 삭제. 빈 상태 안내./focus-card/register):FocusCardActionNodePicker가GET /api/admin/concept/graph/node?onlyFocusCardCandidates=true로 이미 카드가 발급된 ActionNode 자동 제외. 제목/설명/본문 TipTap 에디터 3개./focus-card/$focusCardId): ActionNode read-only chip +nodeType.label배지 + 설명/예시/포인팅 (InlineProblemViewer 2줄). 제목/설명/본문 부분 수정 (POST /focus-card/{id}/content). 헤더 삭제 버튼./focus-card/issuance): 사이드바 선택 학생 + 일자별 발급 카드. 학생 취약점 패널 (GET /api/admin/analytics/concept-history/{studentId}— 시도 5회 이상 + 정답률 낮은 순 top-5). 수동 발급 모달(ActionNode picker), 발급 취소, 자동발급 트리거 (MAT-620 의autoIssueForStudent가 PR #75 vulnerability index 대기 중이라 현재는 빈 결과 가능)./publish/$publishId): 캘린더 Day hover Eye 버튼으로 진입. 문제별 매핑된 카드 chip + 사후 매핑 추가 모달. 모달 후보는GET /api/admin/focus-card/issuance/candidates?studentId&problemId&targetDate— 이 문제의 pointing 버블 액션과 매칭되는 카드만 표시.수정 페이지
/publish/register/$publishDate/$studentId): 선택 세트 펼침 영역에 '출제근거 카드 매핑' 섹션 추가. 일괄 후보 조회 (POST /api/admin/publish/focus-card-link-candidates, read-only 이지만 BE 가 POST 노출 —useQuery('post', ..., { body })패턴). 문제별로 매칭되는 카드만 toggle 가능. 매칭 0개 문제는 회색 안내, toggle hide. linkMap 은 세트 전환 시 보존(현재 세트 item 만 발행 시 filter).PUBLISH_FOCUS_CARD_LINK_001/002BE 검증 실패가 UI에서 사전 차단됨./publish): Day cell hover 액션에 Eye(상세 보기) 버튼 추가 (BarChart3/Trash2 옆).API 추가/확장
focusCard/{getList,getById,post,postContent,delete,postIssuance,getIssuanceByDate,getIssuanceCandidates,deleteIssuance,postAutoIssue},publish/{postFocusCardLink,deleteFocusCardLink,getFocusCardLinkCandidates},analytics/getConceptHistory.conceptGraph/getNode에{ onlyFocusCardCandidates?: boolean }옵션 파라미터 (기본 false, 기존 호출자 무영향).기타
useInvalidate에 4개 invalidator 추가: focus card list/item, issuance by-date, publish detail.useActionNodeDetail/useActionNodeTypeId훅 추가 (hooks/).@types로 이동, hook 을hooks/로 배치, POST 후보 조회를useQuery로 통일.Linear
Test plan
카드 템플릿 CRUD
/focus-card→ '신규 생성' → ActionNode picker 가 카드 없는 노드만 보여줌 → 제목/설명/본문 입력 → 저장 → 목록에 노출/focus-card/$id→ ActionNode 정보(nodeType 배지 + 설명/예시/포인팅) 노출 → 본문 수정 → 저장 토스트학생 카드 발급
발행 inline 매핑
/publish캘린더에서 학생 선택 후 빈 날짜 클릭 → 세트 검색 → 세트 선택 시 출제근거 매핑 섹션 자동 노출발행 사후 매핑
/publish캘린더 Day hover → Eye 클릭 → 상세 진입권한
Known follow-ups
admin_menu시드 마이그레이션에FOCUS_CARD,FOCUS_CARD_ISSUANCE추가 + 컨트롤러 메뉴 ACL 연동.autoIssueForStudent(vulnerability index PR #75 머지 후 활성화). 그때까진 자동발급 결과가 빈 ListResp.PublishResp에studentId추가 (선택) → 발행 상세에서 사이드바 의존 제거 가능.🤖 Generated with Claude Code