Skip to content

refactor(admin): クラス(学級)と課題の表現を徹底整理し、Admin にクラスの状態表示とアーカイブ解除を追加 #1129

Description

@smalruby3-editor-bot

背景 — 運用対応で実際に誤認が起きた

先生から「クラスを誤ってアーカイブしたので復旧してほしい」という依頼を受けて調査したところ、
Admin では「復元済み」に見えるのに、実際には先生の画面から見えないままという状態を運用者が
判別できなかった。原因は Admin における クラス(学級)と課題(1 授業)の表現の混同

ドメインモデルは 2 階層(docs/classroom/README.md):

概念 日本語 テーブル 主キー
Group クラス(学級) ClassroomGroups groupId
Classroom 課題(1 授業) Classrooms classroomId

Classrooms テーブルが「課題」を保持しているというテーブル名自体のねじれが根本にあり、
それが Admin の API 名・UI 文言・タブ名にそのまま漏れている。

現状の問題(コードで確認済み)

1. Admin の「クラス」操作が、実際には課題を操作している

  • packages/admin/src/components/classrooms-view.jsx:247このクラスをアーカイブしますか?
  • :248このクラスを利用中に戻しますか?
  • :420このクラスを復元しますか?
  • :551該当する削除済みクラスはありません。
  • :734 — タブ名 クラス検索

いずれも実際の書き込み先は infra/smalruby-admin/lambda/handler.tshandleSetClassroomStatus
PATCH /admin/classrooms/{classroomId})で、CLASSROOMS_TABLE(= 課題)にしか書かない
クラス(ClassroomGroups)の状態は一切変わらない。

2. 課題一覧・詳細に「親クラスがアーカイブ中」が出ない

mapClassroomForAdmininfra/smalruby-admin/lambda/handler.ts:501 付近)は groupId を返すだけで、
親クラスの status を返さない。そのため:

  • 課題が active なら Admin は 🟢「利用中」と表示する
  • しかし親クラスが archived だと、先生の画面にはその課題は表示されない
  • 運用者は「生きている=先生に見えている」と誤読する ← 今回の誤認の直接原因

3. 誤解を強める文言がある

packages/admin/src/components/classrooms-view.jsx:376

このクラスはまだ存在しています。アーカイブからの復旧は先生自身のクラス管理画面から行えます。

「クラス」と言いつつ判定しているのは課題の生存。親クラスがアーカイブ中のケースにこの文が出ると、
運用者は「先生側で復旧できる状態にある」と読むが、案内すべき操作(クラス一覧 →
「アーカイブ済みのクラス」→「元に戻す」)とは別物になる。

4. クラス(学級)単位の検索・アーカイブ解除が Admin に無い

Admin Lambda が GROUPS_TABLE に触るのは handler.ts:906-912 の 1 箇所だけで、しかも
読み取り(TTL 失効データのスナップショット復元時に、親クラスが「完全に消えているか」の確認)。
liveGroup.Item が返る=生きているが archived のケースは何もしない。

結果、ClassroomGroups.statusactive に戻せるのは先生用 UI の
PATCH /classroom-groups/{groupId}infra/smalruby-classroom/lambda/handler.ts:2474 付近)だけで、
Admin も運用 CLI(bin/restore-classroom.ts = TTL 失効専用)もクラスのアーカイブ解除を持たない
先生が UI 操作に到達できない問い合わせに運用者が対応できない。

やること

A. 用語の徹底監査と統一(最優先・他の作業の前提)

  1. 用語辞典を作るdocs/classroom/README.md または docs/admin/README.md に節を追加):
    クラス(学級)= Group / groupId、課題(1 授業)= Classroom / classroomId
    「組」「学級」「クラスルーム」などの揺れをどう扱うかも決める。
  2. Admin の全文言を監査して、対象が課題なら「課題」、クラスなら「クラス」と正しく呼ぶ。
    packages/admin/src/** のユーザー可視文字列を全て洗い出して確認する(上記 5 箇所は既知だが、
    それだけで終わりにせず網羅する — .claude/rules/ の「パターンは全インスタンスを確認」に従う)。
  3. 識別子・型・関数名・data-testid・API パスについても、課題を指しているのに class
    名乗っているものを洗い出す。互換性の都合で API パス(/admin/classrooms)を変えられない場合は、
    変えない理由と対応表をコードコメント + docs に残す(勝手に破壊的変更をしない)。
  4. teacher UI 側(packages/scratch-gui/src/components/classroom-modal/**)にも同種の揺れが無いか
    確認する。あれば本 EPIC の子 Issue として切り出す。

B. 課題に親クラスの状態を出す

  • Admin API の課題レスポンスに親クラス情報(groupId / クラス名 / groupStatus)を含める。
  • 課題一覧・詳細に「親クラスがアーカイブ中 — 先生には表示されません」という警告バッジ/
    注意文を出す。課題単体が active でも実効的に見えないことが一目で分かること。
  • restore-admin-alive の文言を、課題が生きている場合親クラスがアーカイブ中の場合
    出し分け、後者では先生に案内すべき正確な操作手順(クラス一覧 → アーカイブ済みのクラス →
    元に戻す)を提示する。

C. Admin にクラス(学級)の検索とアーカイブ解除を追加

  • Admin API に クラス(ClassroomGroups)の検索・詳細・status 更新を追加する
    (命名は A の用語辞典に従う。既存の課題向けエンドポイントと明確に区別できるパスにする)。
  • Admin SPA に「クラス」タブ(または既存タブ内の明確な切り替え)を追加し、
    アーカイブ⇄利用中の切り替えと、そのクラスに属する課題の一覧を表示する。
  • 権限・監査ログ(audit(...))は既存の課題操作と同じ扱いにする。
  • 破壊的操作なので確認ステップを踏む(既存の 2 段階確認パターンに合わせる)。

設計上の注意

  • 単一ライターを壊さない: クラスの状態を書く経路が「先生 UI」と「Admin」の 2 つになる。
    どちらも同じ更新形(status + updatedAt)にし、restoredAt 等のスタンプ規約を揃える。
  • TTL を触るときは実行時点から再設定(過去の TTL のまま書くと即再削除される。
    docs/classroom/operations.md の既存規約)。
  • クラスをアーカイブ解除しても、中の課題の status は変えない(今回のケースは課題は最初から
    active。無関係な課題を勝手に復活させない)。
  • 同名クラスが並ぶケースが実際に起きている(Google Classroom 連携での二重作成)。一覧では
    クラス名だけで判断できないため、年度・人数・中の課題名・作成日時を併記して区別できること。

DoD

  • 用語辞典が docs にあり、クラス/課題の定義とテーブル・識別子の対応が明記されている
  • packages/admin/src/** のユーザー可視文言を全数監査し、課題を「クラス」と呼ぶ箇所が 0 件
  • 課題の一覧・詳細で、親クラスがアーカイブ中であることが警告として分かる
  • restore-admin-alive の文言が、課題生存/親クラスアーカイブ中で正しく出し分けられている
  • Admin からクラス(学級)を検索し、アーカイブ解除できる
  • Admin からのクラス状態変更が監査ログに残る
  • unit テスト追加(用語・親クラス状態の表示・クラス status 更新 API)
  • lint / prettier がエラー 0・警告 0
  • docs/admin/README.md(および必要なら docs/classroom/*)を更新
  • UI に視覚的変更があるため docs/admin/screenshots/ を更新(docs/_screenshot-guidelines.md に従う)
  • 今回の誤認シナリオを再現する形で確認(親クラス archived + 課題 active の状態を作り、
    Admin 上で「先生には見えていない」と判別できること)

Metadata

Metadata

Assignees

Labels

🤖 autopilotautopilot が処理対象にした Issue/PR(daemon が付与)🧭 trackingsub-issue に分解済みの親(トラッカー)。作業 item から除外

Type

No type

Projects

Status
In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions