Skip to content

feat: クラスルーム機能 — 作品提出 (Phase 2)#445

Merged
takaokouji merged 18 commits into
developfrom
feature/classroom-submission
Apr 4, 2026
Merged

feat: クラスルーム機能 — 作品提出 (Phase 2)#445
takaokouji merged 18 commits into
developfrom
feature/classroom-submission

Conversation

@takaokouji
Copy link
Copy Markdown

Summary

クラスルーム機能 Phase 2: 作品提出。生徒がエディタから .sb3 ファイルを提出し、教師が座席表グリッドで提出状況を俯瞰できる。

Refs #440, #442

Changes Made

Backend (infra/smalruby-classroom/)

  • S3 バケット: 作品 (.sb3) + サムネイル (PNG) の保存、90日ライフサイクル
  • SubmissionsTable (DynamoDB) + classroomId-memberId GSI
  • sessionToken-index GSI on MembershipsTable — 生徒認証の実装
  • POST /submissions: presigned URL 生成 (sessionToken 認証)
  • GET /submissions: 提出一覧 + サムネイル presigned URL (教師認証)
  • handleListMembers 拡張: 各メンバーの hasSubmission/submittedAt を返却
  • 45 backend unit tests passing

Frontend (packages/scratch-gui/)

  • student-status 画面: 提出状態表示 + 提出/再提出ボタン
  • student-submit-confirm 画面: ステージサムネイルプレビュー + 確認
  • 教師座席表: 提出済みセルが緑色 + ✓マーク(青=未提出、緑=提出済み、グレー=未参加)
  • 詳細ポップアップ: 提出時刻バッジ
  • classroom-api.js: createSubmission, listSubmissions, uploadToPresignedUrl
  • classroom.js reducer: submissionStatus + lastSubmittedAt (localStorage 永続化)

Documentation

  • cdk.context.json をコミット (CDK ベストプラクティス)
  • infra development rules にコミットポリシー追記

takaokouji and others added 4 commits April 4, 2026 13:24
Backend CDK extensions for submission feature (#442):
- S3 bucket for .sb3 and thumbnail storage (90-day lifecycle)
- SubmissionsTable with classroomId-memberId GSI
- sessionToken-index GSI on MembershipsTable for student auth
- New API routes: POST/GET /classrooms/{id}/submissions
- Lambda env vars and permissions for S3 + new table

Also:
- Commit cdk.context.json for all infra projects (CDK best practice)
- Add smalruby-classroom to infra development rules
- Document cdk.context.json commit policy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…gned URLs

Backend implementation for classroom submissions (#442):
- verifySessionToken() using sessionToken-index GSI
- POST /submissions: generate presigned URLs for .sb3 + thumbnail upload
- GET /submissions: list submissions with thumbnail presigned URLs (teacher)
- handleListMembers extended: returns hasSubmission/submittedAt per member
- validateProjectName validation + 6 new unit tests (45 total)
- S3 + DynamoDB dependencies added to package.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend for classroom submissions (#442):
- Student status screen: submission status display + submit/resubmit button
- Submit confirmation phase with stage thumbnail preview
- Upload flow: presigned URL → .sb3 + thumbnail PUT to S3
- Teacher seat grid: green cells with ✓ for submitted students
- Member detail popup: submission time badge
- Reducer: submissionStatus + lastSubmittedAt persisted to localStorage
- API client: createSubmission, listSubmissions, uploadToPresignedUrl
- i18n: ja, ja-Hira, en (13 new keys each)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
React Intl requires statically evaluable message ids. Split dynamic
id into two separate FormattedMessage components with conditional
rendering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 4, 2026

takaokouji and others added 14 commits April 4, 2026 14:47
ESLint no-negated-condition rule disallows `!member ? ... : ...`.
Refactored to if/else assignment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ssion viewing

Teacher improvements:
- Add class deletion with confirmation dialog (soft-delete + member cleanup)
- Show creation date and student count in class list
- Fixed header/footer with scrollable class list in dashboard
- Auto-refresh class detail every 30s (configurable via env var)
- Manual refresh button in class detail
- Show submission thumbnail and "Open in Smalruby" button in member popup

Student improvements:
- Validate join code before seat selection (lookup API)
- Google Classroom-style error box for invalid join codes
- Japanese error messages for seat conflicts and session expiry
- Verify session on status screen open (detect deleted members early)

Backend:
- DELETE /classrooms/{id} — soft-delete with member batch cleanup
- POST /classrooms/lookup — validate join code, return seat info
- POST /classrooms/verify-session — check session token validity
- Add projectUrl (presigned GET) to listSubmissions response
- Add GET to S3 bucket CORS for submission downloads

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Compact class items: reduce vertical padding (0.8rem → 0.5rem) and
margin-bottom (0.5rem → 0.25rem), saving ~11px per item.
Lower dashboard max-height by 20px for more breathing room.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…uto-join

Teacher dashboard:
- Join code displayed in lowercase (Google Classroom style)
- Expand icon (⛶) next to code opens class code display dialog
- Details button pushed to right end of meta row

Class code display dialog:
- Large centered code display with class info footer
- "招待リンクをコピー" copies current URL with classcode= param
- Fullscreen toggle (⛶/⊟) via ReactDOM.createPortal
- × button returns to dashboard

Invite link (classcode URL parameter):
- ?classcode=xxx auto-opens classroom modal on page load
- If already joined to same class: shows status
- If joined to different class: leaves and starts new join flow
- If not joined: validates code and shows seat selection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Teacher role memory:
- Cache idToken at module level across modal close/open
- Skip role selection when reopening modal (go directly to dashboard)
- Logout clears the cache and returns to role selection

Class detail redesign (968px wide modal):
- Two-pane layout: left (info + 10-column seat grid) + right (member detail)
- Right pane shows large thumbnail, project name, "Open in Smalruby" button
- Join code shown in lowercase with expand button for code display
- Members grid fixed at 10 columns per row

Class list simplification:
- Remove expand icon from class list items
- Keep lowercase code display inline

Code display navigation:
- Navigate from class detail (not class list)
- Back button and fullscreen × return to class detail
- Clipboard SVG icon for copy, proper shrink SVG icon

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move back button to top-left of detail area (above two-pane layout)
- Remove duplicate back button from code display (keep only one)
- Show "出席番号XX" instead of "seat-XX" in member detail panel
- Show "リンクをコピーしました" feedback after copying invite link
- Remove loading indicator from members grid to prevent layout shift;
  keep previous state visible during refresh, update on completion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Show "コピーしました" feedback on normal code display (was only fullscreen)
- Left-align back-link buttons (display: block, text-align: left) to
  prevent centering in code-display and detail-layout containers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add user-select: text and cursor: text to all 4 code display locations:
class list item, detail join code, code display dialog, fullscreen code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use ○○○○○○ (white circles) instead of ABC234 to indicate 6-character
input without being confused with actual text. Lighten placeholder color.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Show typed characters in lowercase (matching code display style).
The code is uppercased only when sent to the API.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add CLASSROOM_TTL_DAYS env var (default 30 days) for CDK + Lambda
- S3 lifecycle, DynamoDB TTL, session TTL all use the same value
- Return expiresAt in create/list/get classroom API responses
- Show expiry date in teacher dashboard class list and detail view

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add onOpenClassroomModal to the Redux action props prevention
destructuring in gui.jsx to avoid React "Unknown event handler
property" warning that caused integration test SEVERE log failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
School WiFi sends all 35 students from the same IP. Previous limits
(burst 30, IP rate 5/min) caused HTTP 429 for 6th+ student.

- API Gateway: burst/rate 200 (prod), 50 (stg)
- Lambda IP rate limit: 5 → 50 attempts per 60s
- Client: exponential backoff retry on 429 (500ms→1s→2s + jitter)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@takaokouji takaokouji merged commit 2677a5f into develop Apr 4, 2026
15 checks passed
@takaokouji takaokouji deleted the feature/classroom-submission branch April 4, 2026 15:03
github-actions Bot pushed a commit that referenced this pull request Apr 4, 2026
…room-submission

feat: クラスルーム機能 — 作品提出 (Phase 2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant