Skip to content

[feat] 問題集の詳細ページのグレードアイコンから投票できるようにする#3589

Merged
KATO-Hiro merged 8 commits into
stagingfrom
feature/#3583-workbook-voting
May 30, 2026
Merged

[feat] 問題集の詳細ページのグレードアイコンから投票できるようにする#3589
KATO-Hiro merged 8 commits into
stagingfrom
feature/#3583-workbook-voting

Conversation

@river0525
Copy link
Copy Markdown
Collaborator

@river0525 river0525 commented May 25, 2026

Summary

Changes

  • src/routes/workbooks/[slug]/+page.server.ts:
    • getVoteGradeStatistics() を並列取得して voteStatisticsMap を返す
    • isAtCoderVerified: locals.user?.is_validated === true を返す
    • voteAbsoluteGrade アクションを追加(既存の vote_actions.ts を再利用)
  • src/routes/workbooks/[slug]/+page.svelte:
    • GradeLabelVotableGrade に置き換え
    • isAtCoderVerifiedvoteStatisticsMap$derived で取得
    • 未使用になった getTaskGrade 関数と TaskGrade import を削除

Test plan

  • 問題集詳細ページでグレードアイコンをクリックすると投票ドロップダウンが開くことを確認
  • ログイン済み・AtCoder認証済みユーザーが投票でき、グレード表示がリアルタイムで更新されることを確認
  • 未ログインユーザーにはログイン/アカウント作成のドロップダウンが表示されることを確認
  • AtCoder未認証ユーザーには認証を促すドロップダウンが表示されることを確認
  • pnpm format && pnpm check && pnpm test:unit がすべてパスすることを確認

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 新機能

    • AtCoder検証済ユーザーによる問題への絶対評価(グレード)投票を追加。
  • 改善

    • 問題一覧・タスク行のグレード表示を投票対応の表示に切替え(推定グレード・検証状態を反映)。
    • サーバー側で投票フォームの入力検証を導入し、無効な投票を拒否。
  • テスト

    • 投票入力スキーマの単体テストを追加・整備。
  • その他

    • データ作成時の項目フォールバック処理と型記述を整理。 UI用表示オプションを追加。

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 15e335d4-bd5a-41d1-b6a0-d9af6fed226e

📥 Commits

Reviewing files that changed from the base of the PR and between 179982c and 8369ebe.

📒 Files selected for processing (1)
  • src/features/votes/zod/schema.test.ts

📝 Walkthrough

Walkthrough

voteAbsoluteGrade 用の Zod スキーマを追加し、アクション署名を検証済みデータ受け取りに変更。各ルートで superValidate を適用して invalid は fail を返し、valid は voteAbsoluteGrade に検証済みデータを渡す。workbook ページは isAtCoderVerified を追加し Grade 表示を VotableGrade に置換。関連テスト・seed・型修正を含む。

Changes

投票検証スキーマと vote アクション改築

Layer / File(s) Summary
投票スキーマ定義とテスト
src/features/votes/zod/schema.ts, src/features/votes/zod/schema.test.ts, src/lib/types/auth_forms.ts
voteAbsoluteGradeSchemaVoteAbsoluteGradeInput を追加。taskId の trim/必須・grade は TaskGrade 列挙・PENDING 拒否を定義し、Vitest で有効/境界/無効ケースを網羅的に検証。AuthForm に SchemaShape を追加。
voteAbsoluteGrade アクション署名変更
src/features/votes/actions/vote_actions.ts
アクションが Request を直接解析する方式から data: VoteAbsoluteGradeInput を受け取る署名へ変更。upsert 呼び出しは session.user.userId と data.taskId/data.grade を使用。エラーハンドリングを try/catch に整理。
Route 層でのフォーム検証追加と wiring
src/routes/workbooks/[slug]/+page.server.ts, src/routes/problems/+page.server.ts, src/routes/votes/[slug]/+page.server.ts
各ルートで superValidate(request, zod4(voteAbsoluteGradeSchema)) による検証を導入。無効時は fail(BAD_REQUEST, { form })、有効時は voteAbsoluteGrade({ locals, data: form.data }) を呼ぶ。workbooks の load に isAtCoderVerified を追加。
Workbook ページ UI 置換
src/routes/workbooks/[slug]/+page.svelte, src/features/votes/components/VotableGrade.svelte, src/features/tasks/components/contest-table/TaskTableBodyCell.svelte
Grade 表示を GradeLabel/RelativeEvaluationBadge から VotableGrade へ置換。voteStatisticsMapisAtCoderVerified を derived で準備し、各タスクに taskResult・ログイン/検証状態・推定 grade を渡す。VotableGrade にレイアウト用 props を追加。
支援変更(seed とテストの型)
prisma/seed.ts, src/features/workbooks/stores/replenishment_workbook.test.ts
classifyContest の nullish を Prisma に undefined として渡すよう修正。テストのモック型を vitest の Mock に合わせて修正。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • KATO-Hiro

投票フォームは鍵、スキーマで錠をかけ
Route は検証を受けて静かに手渡す
UI は VotableGrade に変わり映えし
seed はそっと undefined を受け入れ
テストはモックで真実を確かめる

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR のタイトルが変更内容の主要な機能(問題集詳細ページのグレードアイコンから投票可能にする)を明確に説明しており、提供されたコード変更と一致しています。
Linked Issues check ✅ Passed PR の実装は Issue #3583 の要件(グレードアイコンからの投票機能実装、投票統計取得、認証状態対応、サーバーアクション実装)をすべて満たしています。
Out of Scope Changes check ✅ Passed スキーマ検証の追加、テストの更新、型定義の整理など、すべての変更は Issue #3583 の投票機能実装に関連または必要な変更です。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#3583-workbook-voting

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/workbooks/`[slug]/+page.server.ts:
- Around line 41-44: The page is loading global vote stats via
getVoteGradeStatistics(), causing high payload; change to fetch only stats for
tasks in this workbook by replacing the call with a new service that accepts
task IDs (e.g., getVoteGradeStatisticsByTaskIds). Collect the task IDs from
workBook.workBookTasks (map to id strings), call
getVoteGradeStatisticsByTaskIds(taskIds) where getVoteGradeStatistics() was
used, and update imports to use the new function (implement
getVoteGradeStatisticsByTaskIds in
src/features/votes/services/vote_statistics.ts to query votedGradeStatistics
where taskId in taskIds and return a map keyed by taskId).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1f7da413-3c8e-4a39-8a39-d26527c8ffe1

📥 Commits

Reviewing files that changed from the base of the PR and between 2abae1a and 38b1e67.

📒 Files selected for processing (2)
  • src/routes/workbooks/[slug]/+page.server.ts
  • src/routes/workbooks/[slug]/+page.svelte

Comment thread src/routes/workbooks/[slug]/+page.server.ts
@river0525
Copy link
Copy Markdown
Collaborator Author

river0525 commented May 25, 2026

@KATO-Hiro
本PRの修正は #3588 のマージ後に行います。(リベースが必要なため)
それまではレビューをしていただかなくても問題ございません。

@KATO-Hiro
Copy link
Copy Markdown
Collaborator

@river0525
承知いたしました

先ほど、#3588 をマージいたしました
お手数をおかけしますが、リベースをお願いできますでしょうか?

Replace GradeLabel with VotableGrade so users can vote directly from the
grade icon on the workbook detail page. Fetches vote statistics in parallel
with task results and adds the voteAbsoluteGrade form action.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@river0525 river0525 force-pushed the feature/#3583-workbook-voting branch from 38b1e67 to a05606e Compare May 25, 2026 16:58
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/workbooks/`[slug]/+page.server.ts:
- Around line 61-63: The voteAbsoluteGrade action currently delegates without
Superforms+Zod validation; update +page.server.ts to perform server-side
validation with Superforms and your Zod schema before calling
voteAbsoluteGradeAction: import the Zod schema for the vote form and Superforms'
superValidate, call await superValidate(request, voteAbsoluteGradeSchema) (or
equivalent) to get validated.data, then pass that validated payload into
voteAbsoluteGradeAction (e.g., voteAbsoluteGradeAction({ request, locals, data:
validated.data })), and adjust voteAbsoluteGradeAction signature to accept and
use the validated data instead of raw request parsing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3fac380a-ba96-4524-83e0-53203b2f7eb3

📥 Commits

Reviewing files that changed from the base of the PR and between 38b1e67 and a05606e.

📒 Files selected for processing (2)
  • src/routes/workbooks/[slug]/+page.server.ts
  • src/routes/workbooks/[slug]/+page.svelte

Comment thread src/routes/workbooks/[slug]/+page.server.ts
@river0525
Copy link
Copy Markdown
Collaborator Author

river0525 commented May 27, 2026

@KATO-Hiro
リベース及び、AIレビューへの対応が完了いたしました。
ご確認よろしくお願いいたします。

追加のレビューが来たので対応いたします。

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/votes/zod/schema.ts`:
- Line 5: taskId の Zod スキーマは現在 z.string().min(1)
で空白のみが通ってしまうので、src/features/votes/zod/schema.ts の taskId 定義を
z.string().trim().min(1) に変更して前後の空白を除去し空文字を拒否するようにし、既存のバリデーションエラー処理(Zod
のエラーハンドリング)で不正入力を 400 レスポンスに返すことを確認してください。

In `@src/lib/types/auth_forms.ts`:
- Line 22: The current recursive type SchemaShape = { [key: string]: SchemaShape
} forces every node to be an object of the same shape and prevents leaf nodes
like { type: 'string' }; update SchemaShape to allow either a nested map or a
leaf descriptor (e.g., permit objects like { type: string } at leaves) so
form.shape can be typed as { username: { type: 'string' }, password: { type:
'string' } } without casts; change the definition of SchemaShape (and any uses)
to something like a union between a map and a leaf descriptor so callers (and
auth_forms.test.ts) no longer need as unknown casts.

In `@src/lib/utils/auth_forms.ts`:
- Line 167: createBaseAuthForm's fallback `shape: {}` breaks compatibility with
tests expecting a username/password schema; replace the empty fallback shape in
createBaseAuthForm with the expected schema (username and password entries with
type 'string') so the function returns a shape matching { username: { type:
'string' }, password: { type: 'string' } } when no custom shape is provided.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b1713abb-a947-4ce3-96e1-7f436b42a02d

📥 Commits

Reviewing files that changed from the base of the PR and between a05606e and 5e05054.

📒 Files selected for processing (9)
  • prisma/seed.ts
  • src/features/votes/actions/vote_actions.ts
  • src/features/votes/zod/schema.ts
  • src/features/workbooks/stores/replenishment_workbook.test.ts
  • src/lib/types/auth_forms.ts
  • src/lib/utils/auth_forms.ts
  • src/routes/problems/+page.server.ts
  • src/routes/votes/[slug]/+page.server.ts
  • src/routes/workbooks/[slug]/+page.server.ts

Comment thread src/features/votes/zod/schema.ts Outdated
Comment thread src/lib/types/auth_forms.ts Outdated
Comment thread src/lib/utils/auth_forms.ts Outdated
- Add .trim() to taskId in voteAbsoluteGradeSchema to reject whitespace-only strings
- Fix SchemaShape type from recursive self-reference to Record<string, unknown> so leaf values like { type: 'string' } are representable
- Restore shape fallback in createBaseAuthForm to { username: { type: 'string' }, password: { type: 'string' } } — was emptied when recursive type was introduced

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@river0525
Copy link
Copy Markdown
Collaborator Author

@KATO-Hiro
レビューへの対応が完了いたしました。
改めまして、ご確認よろしくお願いいたします。

@KATO-Hiro
Copy link
Copy Markdown
Collaborator

ご対応いただき、ありがとうございます
拝見いたします

KATO-Hiro and others added 2 commits May 28, 2026 14:00
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
KATO-Hiro and others added 2 commits May 29, 2026 23:31
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@KATO-Hiro
Copy link
Copy Markdown
Collaborator

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

✅ Actions performed

Reviews resumed.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/votes/zod/schema.test.ts`:
- Around line 64-70: The tests 'missing grade is rejected' and 'missing taskId
is rejected' currently pass keys with undefined which doesn't assert a truly
missing key; change the test inputs for the validate function so the key is
omitted entirely (call validate({ taskId: 'abc408_a' }) for the missing grade
test and validate({ grade: TaskGrade.Q5 }) for the missing taskId test) so the
schema's required-key behavior is actually exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 50e4bf15-b3d2-4471-b9fb-d7b6b5764acb

📥 Commits

Reviewing files that changed from the base of the PR and between b04ba39 and 179982c.

📒 Files selected for processing (3)
  • src/features/tasks/components/contest-table/TaskTableBodyCell.svelte
  • src/features/votes/components/VotableGrade.svelte
  • src/features/votes/zod/schema.test.ts

Comment thread src/features/votes/zod/schema.test.ts
… tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@KATO-Hiro KATO-Hiro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@river0525

PR 作成ありがとうございます

LGTM です

マージします

@KATO-Hiro KATO-Hiro merged commit 8ff4b32 into staging May 30, 2026
3 checks passed
@KATO-Hiro KATO-Hiro deleted the feature/#3583-workbook-voting branch May 30, 2026 00:17
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.

[feat] 問題集の詳細ページ: グレードアイコンから投票できるか調査 + 実現可能なら投票できるようにしましょう

2 participants