feat(career): 未ログインのお試し入力動線とサイドバー/ヘッダーUIを刷新#312
Conversation
- 未ログインでも職務経歴書を入力可能にし、保存/プレビュー/ダウンロード/ GitHub・ブログ連携への遷移時にログイン促進モーダルを表示する - 入力ドラフトを sessionStorage に退避し OAuth 往復後に復元(新規ユーザーは 自動保存、既存ユーザーはフォーム復元のみ)。useCareerDraftRestore / careerDraft を追加 - AuthenticatedLayout を SidebarLayout に統合し /career を認証有無の両対応に (未ログインは連携メニューを押下でログイン誘導、職務経歴書のみ入力可) - 未ログインのフッターは username 位置にログインボタン+▲メニュー (ダークモード切替 / Issue報告 / 著作権表示)を配置 - ヘッダーの保存/プレビュー/PDF出力/Markdown出力/削除をアイコン化 (日本語 aria-label を維持) - useDocumentForm に skipLoad を追加し save() を Promise<boolean> 化 - 関連ユニットテスト(careerDraft / useCareerDraftRestore / LoginPromptModal / useDocumentForm)と認証フロー E2E を追加・更新 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (5)
📝 WalkthroughWalkthroughThis PR implements unauthenticated trial access to the career form with draft persistence across OAuth flow. It adds a shared login-prompt modal system, refactors the sidebar layout to support both auth states, implements sessionStorage-backed draft utilities and a restore hook, updates form loading/save for anonymous mode, and adds E2E tests covering the full round-trip. ChangesUnauthenticated Career Form with Draft Persistence
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
frontend/e2e/auth.spec.ts (1)
24-250:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse shared message constants in E2E selectors/assertions instead of inline literals.
The changed tests repeatedly hardcode UI labels/headings (e.g.,
ログイン,GitHub連携,ログインして保存しましょう,GitHubでログイン). This drifts from SSoT and makes tests brittle when message constants are updated centrally.As per coding guidelines, "In frontend code, follow message management rules from
.claude/rules/frontend/messages.md- message literals are forbidden and must use Single Source of Truth (SSoT)".🤖 Prompt for 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. In `@frontend/e2e/auth.spec.ts` around lines 24 - 250, Replace hardcoded UI text in these tests (e.g., the headings/buttons/placeholders used in the tests "ルート / ..." and "ログイン画面" scenarios) with the project's shared message constants: import the centralized messages module and use the appropriate constants in all getByRole/getByPlaceholder assertions and clicks instead of inline literals like "ログイン", "GitHub連携", "ブログ連携", "ログインして保存しましょう", "GitHubでログイン", "プレビュー", and "例: 山田 太郎". Update every occurrence inside the tests (references include the test blocks with getByRole and getByPlaceholder calls and the sessionStorage key "career_draft") to reference the SSoT constant names from the messages export so tests follow the message management rules.Source: Coding guidelines
frontend/src/components/UserMenu.tsx (1)
49-103:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReplace remaining inline menu labels with centralized messages.
Line 50 (
ダークモード), Line 64 (ログアウト), and Line 101 fallback (Menu) are hardcoded strings in a frontend component. These should come from SSoT constants like the rest of the menu text.As per coding guidelines, "In frontend code, follow message management rules from
.claude/rules/frontend/messages.md- message literals are forbidden and must use Single Source of Truth (SSoT)".🤖 Prompt for 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. In `@frontend/src/components/UserMenu.tsx` around lines 49 - 103, Replace the remaining hardcoded menu labels in UserMenu.tsx with SSoT message constants: change "ダークモード" -> UI_MESSAGES.DARK_MODE where used in the theme toggle (near onToggleTheme), "ログアウト" -> UI_MESSAGES.LOGOUT in the logout button (onLogout), and the fallback "Menu" -> UI_MESSAGES.MENU (used with username || ...) in the triggerName; ensure UI_MESSAGES is imported and add the corresponding keys to the centralized messages file if they don't exist.Source: Coding guidelines
🧹 Nitpick comments (2)
frontend/src/utils/careerDraft.ts (1)
28-39: 💤 Low valueConsider adding runtime validation for the loaded draft.
The type assertion
as CareerFormStateon line 33 casts the parsed JSON without validating its structure. If the stored data is malformed (e.g., from a previous version or manual tampering), downstream code may encounter unexpected runtime errors when accessing expected fields.Consider adding a lightweight schema check or version marker to ensure the loaded draft matches the expected structure.
🛡️ Example validation approach
export function loadCareerDraft(): CareerFormState | null { const raw = sessionStorage.getItem(CAREER_DRAFT_KEY); if (!raw) return null; try { - return JSON.parse(raw) as CareerFormState; + const parsed = JSON.parse(raw); + // Basic structural validation + if (!parsed || typeof parsed !== 'object' || typeof parsed.full_name !== 'string') { + console.warn("職務経歴書ドラフトの形式が不正です"); + clearCareerDraft(); + return null; + } + return parsed as CareerFormState; } catch (error) {🤖 Prompt for 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. In `@frontend/src/utils/careerDraft.ts` around lines 28 - 39, The loadCareerDraft function currently casts parsed JSON to CareerFormState without validation, which can cause runtime errors for malformed drafts; update loadCareerDraft to perform a lightweight runtime schema check (or version marker) after JSON.parse — verify required top-level fields and their types (e.g., expected strings, arrays, nested objects) and any draft version key, call clearCareerDraft() and return null if validation fails, otherwise return the parsed value typed as CareerFormState; reference loadCareerDraft, CAREER_DRAFT_KEY, CareerFormState, and clearCareerDraft when locating where to add the checks.frontend/src/components/forms/CareerResumeForm.tsx (1)
99-125: ⚖️ Poor tradeoffConsider debouncing the draft save effect to reduce sessionStorage writes.
The effect on lines 118-125 writes to sessionStorage on every form change when unauthenticated. For users entering large amounts of data quickly, this could result in many synchronous storage writes.
While the current implementation is functional and safe (with try/catch), consider debouncing the
saveCareerDraftcall to reduce write frequency (e.g., every 500ms after the last change) for improved performance.🤖 Prompt for 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. In `@frontend/src/components/forms/CareerResumeForm.tsx` around lines 99 - 125, The effect currently writes to sessionStorage on every form change; debounce the save to reduce synchronous writes by wrapping the saveCareerDraft call in a debounced timer (e.g., 500ms) inside the existing useEffect that watches isAuthenticated and form: when !isAuthenticated and form.full_name.trim() start/refresh a setTimeout to call saveCareerDraft(form) after 500ms, clear the timeout on subsequent runs and in the effect cleanup to avoid duplicate writes, and still call clearCareerDraft immediately when full_name is empty; reference saveCareerDraft, clearCareerDraft, useEffect, form, and isAuthenticated when locating the change.
🤖 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 `@frontend/src/components/SidebarLayout.tsx`:
- Around line 96-181: The sidebar contains hardcoded UI strings in SidebarLayout
(e.g., NavLink children "職務経歴書", "GitHub連携", "ブログ連携", the button aria-label
"GitHub連携オプション", tooltip titles using AUTH_PROMPT_MESSAGES.LOGIN_REQUIRED_HINT,
the subbutton "連携実行", and the checkbox label "フォークしたリポジトリを含む"); replace each
literal with references to centralized message constants (import from your
messages/messages.ts or the agreed SSoT module) and use those constants in the
NavLink text, button aria-label, checkbox label, and title props (e.g., replace
literal strings used in NavLink, the Chevron button aria-label, the
triggerGitHubLink button text, and the checkbox text with
MESSAGE_KEYS.GITHUB_LINK, MESSAGE_KEYS.GITHUB_OPTIONS_LABEL,
MESSAGE_KEYS.INCLUDE_FORKS, etc.) so all UI text originates from the single
source of truth.
---
Outside diff comments:
In `@frontend/e2e/auth.spec.ts`:
- Around line 24-250: Replace hardcoded UI text in these tests (e.g., the
headings/buttons/placeholders used in the tests "ルート / ..." and "ログイン画面"
scenarios) with the project's shared message constants: import the centralized
messages module and use the appropriate constants in all
getByRole/getByPlaceholder assertions and clicks instead of inline literals like
"ログイン", "GitHub連携", "ブログ連携", "ログインして保存しましょう", "GitHubでログイン", "プレビュー", and "例: 山田
太郎". Update every occurrence inside the tests (references include the test
blocks with getByRole and getByPlaceholder calls and the sessionStorage key
"career_draft") to reference the SSoT constant names from the messages export so
tests follow the message management rules.
In `@frontend/src/components/UserMenu.tsx`:
- Around line 49-103: Replace the remaining hardcoded menu labels in
UserMenu.tsx with SSoT message constants: change "ダークモード" ->
UI_MESSAGES.DARK_MODE where used in the theme toggle (near onToggleTheme),
"ログアウト" -> UI_MESSAGES.LOGOUT in the logout button (onLogout), and the fallback
"Menu" -> UI_MESSAGES.MENU (used with username || ...) in the triggerName;
ensure UI_MESSAGES is imported and add the corresponding keys to the centralized
messages file if they don't exist.
---
Nitpick comments:
In `@frontend/src/components/forms/CareerResumeForm.tsx`:
- Around line 99-125: The effect currently writes to sessionStorage on every
form change; debounce the save to reduce synchronous writes by wrapping the
saveCareerDraft call in a debounced timer (e.g., 500ms) inside the existing
useEffect that watches isAuthenticated and form: when !isAuthenticated and
form.full_name.trim() start/refresh a setTimeout to call saveCareerDraft(form)
after 500ms, clear the timeout on subsequent runs and in the effect cleanup to
avoid duplicate writes, and still call clearCareerDraft immediately when
full_name is empty; reference saveCareerDraft, clearCareerDraft, useEffect,
form, and isAuthenticated when locating the change.
In `@frontend/src/utils/careerDraft.ts`:
- Around line 28-39: The loadCareerDraft function currently casts parsed JSON to
CareerFormState without validation, which can cause runtime errors for malformed
drafts; update loadCareerDraft to perform a lightweight runtime schema check (or
version marker) after JSON.parse — verify required top-level fields and their
types (e.g., expected strings, arrays, nested objects) and any draft version
key, call clearCareerDraft() and return null if validation fails, otherwise
return the parsed value typed as CareerFormState; reference loadCareerDraft,
CAREER_DRAFT_KEY, CareerFormState, and clearCareerDraft when locating where to
add the checks.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 587a2ed8-8c7b-4be4-9c8d-0b4133029545
📒 Files selected for processing (29)
frontend/e2e/auth.spec.tsfrontend/src/App.module.cssfrontend/src/App.tsxfrontend/src/components/AuthenticatedLayout.tsxfrontend/src/components/SidebarLayout.tsxfrontend/src/components/UserMenu.module.cssfrontend/src/components/UserMenu.tsxfrontend/src/components/auth/LoginPromptModal.module.cssfrontend/src/components/auth/LoginPromptModal.test.tsxfrontend/src/components/auth/LoginPromptModal.tsxfrontend/src/components/auth/LoginPromptProvider.tsxfrontend/src/components/auth/loginPromptContext.tsfrontend/src/components/forms/CareerResumeForm.module.cssfrontend/src/components/forms/CareerResumeForm.tsxfrontend/src/components/icons/EyeIcon.tsxfrontend/src/components/icons/GitHubMarkIcon.tsxfrontend/src/components/icons/MarkdownDownloadIcon.tsxfrontend/src/components/icons/PdfDownloadIcon.tsxfrontend/src/components/icons/SaveIcon.tsxfrontend/src/constants/messages.tsfrontend/src/hooks/career/useCareerDraftRestore.test.tsfrontend/src/hooks/career/useCareerDraftRestore.tsfrontend/src/hooks/useDocumentForm.test.tsfrontend/src/hooks/useDocumentForm.tsfrontend/src/pages/CareerPage.tsxfrontend/src/router/routes.tsxfrontend/src/styles.cssfrontend/src/utils/careerDraft.test.tsfrontend/src/utils/careerDraft.ts
💤 Files with no reviewable changes (1)
- frontend/src/components/AuthenticatedLayout.tsx
- SidebarLayout / UserMenu のハードコード UI 文言を UI_MESSAGES に集約 - loadCareerDraft にドラフト形式の軽量バリデーションを追加し、 旧フォーマット/破損データは破棄して null を返す(復元時のクラッシュ防止) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
概要
未ログインユーザーが「ログイン強制」ではなく、まず職務経歴書を入力できる try before sign up 型の動線に刷新します。価値を体験してから(入力済みデータという sunk cost を抱えた状態で)ログインを促すことで登録への転換を狙います。あわせてヘッダー/フッターの UI を整理しました。
主な変更
未ログインのお試し入力
/careerを認証有無の両対応に変更(AuthenticatedLayoutをSidebarLayoutに統合、/careerをPrivateRoute外へ)。LoginPromptProvider)を表示。sessionStorageに退避し、OAuth 往復後に復元。新規ユーザーは自動保存、既存ユーザーはフォーム復元のみ(上書きは本人操作)。UI
UserMenu。aria-label/titleを維持)。共通フック
useDocumentFormにskipLoad(匿名モードでloadLatestを呼ばない)を追加し、save()をPromise<boolean>化。破壊的変更 / 設計メモ
AuthenticatedLayout.tsxを削除しSidebarLayout.tsxに統合。ルーティング構成を変更。テスト
make ci通過(backend lint/test + frontend lint/test 288 + build)。auth.spec.tsの旧「/login リダイレクト」アサーションを新フローへ置換し、連携/プレビュー押下→モーダル、ドラフト復元の往復テストを追加。careerDraft/useCareerDraftRestore/LoginPromptModal/useDocumentForm。🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Refactor
Tests
UI