Skip to content

feat(resume): 職務経歴書に連絡先(email / github)を追加し PDF/Markdown でリンク表示#319

Merged
yusuke0610 merged 2 commits into
mainfrom
feat/resume-contact-email-github
Jun 10, 2026
Merged

feat(resume): 職務経歴書に連絡先(email / github)を追加し PDF/Markdown でリンク表示#319
yusuke0610 merged 2 commits into
mainfrom
feat/resume-contact-email-github

Conversation

@yusuke0610

@yusuke0610 yusuke0610 commented Jun 10, 2026

Copy link
Copy Markdown
Owner

概要

職務経歴書(Resume)に連絡先フィールド(email / github)を追加し、PDF・Markdown 出力でリンク表示できるようにします。あわせて、保存前の差分確認モーダルの表示崩れを修正します。

変更内容

連絡先フィールドの追加(contact / email / github)

  • schemas/resume.py: email(必須)/ github_url(任意・https://github.com/ 始まりを検証)を追加
  • migration 0043resumes テーブルに contact カラムを追加
  • フォーム(CareerBasicInfoSection)/ payloadBuilders / dirty 判定 / 差分プレビューに email・github を反映
  • OpenAPI 型再生成(frontend/src/api/generated.ts

PDF / Markdown のラベル・リンク表示

  • 連絡先ラベルを email / github に統一
  • github URL をハイパーリンク表示に
    • PDF: <a class="meta-link">(青 #0066cc・下線)
    • Markdown: [URL](URL) 記法
  • data-fp="github_url"<a> に付与(差分ハイライトは [data-fp] 全要素対象のため維持)

差分確認モーダルの表示修正

  • CareerDiffModal の変更サマリが多いとき、行が暗黙の auto で伸びてモーダル(overflow:hidden)から clip され、ロールバック/操作ボタンが見えなくなる不具合を修正
  • .bodygrid-template-rows: minmax(0, 1fr) で行高を固定し、サマリ列の overflow-y:auto を有効化(全件スクロール可能に)

テスト

  • make ci 全 pass(backend lint/test、frontend lint/test 303 件、build)
  • codegen drift なし
  • PDF/Markdown 生成テストにラベル・リンク化の assert を追加

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added email and GitHub URL contact fields to resume profiles.
    • Email is required and validates proper format.
    • GitHub URL is optional; when provided, must be a valid github.com URL.
    • Contact information displays in resume outputs (PDF and Markdown formats).
  • Database

    • Added email and GitHub URL columns to resume records for persistent storage.

- resume schema に email(必須)/ github_url(任意・https://github.com/ 始まり検証)を追加
- migration 0043 で resumes テーブルに contact カラムを追加
- フォーム(CareerBasicInfoSection)/ payloadBuilders / dirty 判定 / 差分プレビューに
  email・github を反映
- PDF / Markdown 生成の連絡先ラベルを email / github に統一し、github URL を
  ハイパーリンク表示にする(PDF: <a class="meta-link"> 青・下線、Markdown: [URL](URL) 記法)
- 差分確認モーダルの変更サマリを全件スクロール可能にし、変更が多くても
  ロールバック/操作ボタンが clip されないよう修正

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@yusuke0610, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 6 minutes and 4 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d74d05b-1e65-4125-9d99-7623fd8c5042

📥 Commits

Reviewing files that changed from the base of the PR and between 8e122b2 and 262a1a0.

📒 Files selected for processing (4)
  • backend/app/schemas/resume.py
  • backend/tests/test_endpoints.py
  • backend/tests/test_markdown_generator.py
  • frontend/src/utils/careerDiff.test.ts
📝 Walkthrough

Walkthrough

This PR adds email and GitHub URL contact fields to resumes across the entire stack. Users can now enter their email and GitHub profile URL in the resume form; the fields are validated (email format, github.com prefix), persisted to the database, and rendered in PDF and Markdown outputs.

Changes

Resume Contact Fields Feature

Layer / File(s) Summary
Database schema and ORM model
backend/alembic_migrations/versions/0043_add_contact_to_resumes.py, backend/app/models/resume.py
Alembic migration adds email and github_url columns (String 255, non-null with empty-string defaults) for backward compatibility. Resume ORM model maps both fields as required Mapped[str] attributes.
Backend validation, data flow, and API integration
backend/app/schemas/resume.py, backend/app/messages.json, backend/app/repositories/resume.py, backend/app/routers/resumes.py
Schema validation enforces email regex pattern and github.com URL prefix when non-empty; repository applies payload fields; router includes contact fields in payload; localized error messages for validation failures.
Backend output generation
backend/app/services/markdown/generators/resume_generator.py, backend/app/services/pdf/generators/resume_generator.py, backend/app/services/pdf/templates/resume.css
Markdown and PDF generators conditionally render contact section with email and GitHub hyperlink after name. PDF template adds .meta-link styling for blue underlined GitHub links.
Frontend API types, form state, and payload validation
frontend/src/api/generated.ts, frontend/src/formTypes.ts, frontend/src/formMappers.ts, frontend/src/constants/messages.ts, frontend/src/payloadBuilders.ts
Generated TypeScript types extend resume DTOs with email and github_url. Form state initializers and mappers populate contact fields. Payload builders validate email and GitHub URL with field-specific error locators; constants define validation messages and diff labels.
Frontend UI components and form wiring
frontend/src/components/forms/sections/CareerBasicInfoSection.tsx, frontend/src/components/forms/CareerResumeForm.tsx, frontend/src/components/forms/CareerDiffModal.module.css
CareerBasicInfoSection component adds email and GitHub URL input fields with separate onChange handlers and optional dirty flags. Inputs include Skeleton loading and aria-invalid accessibility. CareerResumeForm wires new fields and dirty states. Modal CSS enforces grid row constraints for scrolling.
Frontend dirty tracking, diff detection, and draft persistence
frontend/src/hooks/career/useCareerDirty.ts, frontend/src/utils/careerDiff.ts, frontend/src/utils/careerDraft.ts
Dirty-map type tracks email and github_url changes; diff utility includes scalar items for rollback. Draft validator requires both contact fields as strings, invalidating legacy drafts.
Comprehensive test coverage
backend/tests/conftest.py, backend/tests/security/_helpers.py, backend/tests/auth/test_endpoints.py, backend/tests/test_endpoints.py, backend/tests/test_markdown_generator.py, backend/tests/test_pdf_generator.py, backend/tests/test_schemas.py, frontend/src/payloadBuilders.test.ts, frontend/src/hooks/career/useCareerDirty.test.ts, frontend/src/hooks/career/useCareerExperienceMutators.test.ts, frontend/src/hooks/career/useResumeDiffPreview.test.ts, frontend/src/utils/careerDiff.test.ts, frontend/e2e/auth.spec.ts, frontend/e2e/career-dirty-indicator.spec.ts, frontend/e2e/career-field-modal.spec.ts, frontend/tests/payloadBuilders.test.cjs
Backend fixtures include email in payloads. Endpoint tests verify round-tripping and validation errors (422 for invalid formats). Generator tests assert conditional rendering. Schema tests validate email required/format and github_url optional/format with parametrized cases. Frontend payload tests verify email validation, github.com prefix check, and trimming. Hook and utility tests updated with contact fields. E2E mocks include contact fields in API responses and sessionStorage drafts.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • yusuke0610/devforge#261: Refactored CareerBasicInfoSection and CareerResumeForm component structure, which this PR extends directly to add email and GitHub URL inputs.
  • yusuke0610/devforge#306: Modified backend resume HTML generation and data-fp annotation markers; this PR adds email and github_url contact rendering that integrates with the same preview/diff HTML path.
  • yusuke0610/devforge#312: Updated E2E test mocks for /api/resumes payload and sessionStorage draft seeding; this PR adds the same email and github_url fields to those fixtures.

🐰 A rabbit hops through the garden with glee,
E-mail and GitHub for all to see!
Contact fields bloom where resumes grow,
Hop, hop, hooray—the PR steals the show! 🌱✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.04% 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 The title clearly and concisely describes the main feature addition: adding contact fields (email/GitHub) to resumes with link display in PDF/Markdown outputs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/resume-contact-email-github

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
frontend/src/utils/careerDiff.test.ts (1)

17-18: ⚡ Quick win

Add direct diff assertions for the new contact fields.

Line 17 and Line 18 update fixtures, but this suite still doesn’t explicitly assert email/github_url diff detection and rollback paths.

Suggested test additions
 describe("buildCareerChanges", () => {
+  it("email の修正を modified として検出する", () => {
+    const baseline = buildForm();
+    const form = buildForm();
+    form.email = "sato@example.com";
+
+    const changes = buildCareerChanges(form, baseline);
+    const emailChange = changes.find((c) => JSON.stringify(c.path) === JSON.stringify(["email"]));
+    expect(emailChange).toMatchObject({
+      kind: "modified",
+      oldValue: "yamada@example.com",
+      newValue: "sato@example.com",
+    });
+  });
+
+  it("github_url の修正を modified として検出し、ロールバックできる", () => {
+    const baseline = buildForm();
+    const form = buildForm();
+    form.github_url = "https://github.com/example";
+
+    const changes = buildCareerChanges(form, baseline);
+    const ghChange = changes.find((c) => JSON.stringify(c.path) === JSON.stringify(["github_url"]));
+    expect(ghChange?.kind).toBe("modified");
+
+    const rolled = ghChange!.rollback(form);
+    expect(rolled.github_url).toBe("");
+  });
+});
🤖 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/careerDiff.test.ts` around lines 17 - 18, Update the
careerDiff.test.ts suite to explicitly assert that changes to the new contact
fields are detected and rolled back: add assertions that a diff produced for the
fixture with email and github_url changes contains entries for "email" and
"github_url" and that invoking the same rollback helper used in this test file
(the test's diff detection and rollback functions, e.g., computeCareerDiff /
applyRollback or the local helpers already used in the file) restores the
original email and github_url values; place these checks alongside the existing
diff detection/rollback tests in careerDiff.test.ts so both detection and
rollback paths for email and github_url are covered.
backend/tests/test_markdown_generator.py (1)

57-71: ⚡ Quick win

Narrow the omission assertion to the rendered field token.

Line 70 uses assert "github" not in md, which is broader than the behavior under test. Checking the exact rendered token is more stable.

Proposed test tightening
-    assert "github" not in md
+    assert "**github:**" not in md
+    assert "[https://github.com/" not in md
🤖 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 `@backend/tests/test_markdown_generator.py` around lines 57 - 71, The test
test_contact_fields_omitted_when_empty calls build_resume_markdown but asserts
broadly with assert "github" not in md; tighten this to assert the exact
rendered field token is absent (e.g., the label or markdown token your renderer
emits such as "GitHub:" or the specific link label) so it only checks that the
rendered contact field is omitted; update the assertion in
test_contact_fields_omitted_when_empty to look for that exact token instead of
the substring "github".
backend/tests/test_endpoints.py (1)

67-126: ⚡ Quick win

Extract a shared payload builder in the new contact validation tests.

test_resume_contact_round_trips, test_resume_rejects_invalid_email_via_api, and test_resume_rejects_invalid_github_url_via_api repeat nearly identical resume JSON. Reusing make_resume_payload(...) from conftest.py will reduce drift when required fields change again.

♻️ Proposed refactor
-from conftest import auth_header
+from conftest import auth_header, make_resume_payload
...
-    resp = client.post(
-        "/api/resumes",
-        json={
-            "full_name": "連絡先 太郎",
-            "email": "contact@example.com",
-            "github_url": "https://github.com/contact-taro",
-            "career_summary": "要約",
-            "self_pr": "自己PR",
-            "experiences": [],
-            "qualifications": [],
-        },
-        headers=headers,
-    )
+    resp = client.post(
+        "/api/resumes",
+        json=make_resume_payload(
+            full_name="連絡先 太郎",
+            email="contact@example.com",
+            github_url="https://github.com/contact-taro",
+            career_summary="要約",
+            self_pr="自己PR",
+            experiences=[],
+            qualifications=[],
+        ),
+        headers=headers,
+    )

As per coding guidelines, backend/**/*.py changes should “apply DRY principles and code duplication policies defined in .claude/rules/common/duplication.md.”

🤖 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 `@backend/tests/test_endpoints.py` around lines 67 - 126, The three tests
test_resume_contact_round_trips, test_resume_rejects_invalid_email_via_api, and
test_resume_rejects_invalid_github_url_via_api duplicate the same resume JSON;
replace the inline JSON in each with calls to the shared helper
make_resume_payload(...) from conftest.py (override specific fields like email
or github_url per-test as needed), update usages in those tests to pass the
returned dict to client.post(..., json=...), and remove the duplicated literal
payloads so tests DRYly reuse the single payload builder.

Source: Coding guidelines

🤖 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 `@backend/app/schemas/resume.py`:
- Around line 245-260: The validators validate_email and validate_github_url
currently call strip() for checks but return the original untrimmed value,
leaving surrounding whitespace in stored/contact fields; update both methods to
return the trimmed string (use the already-created stripped variable for github
and use a trimmed value for email before matching) so normalized values (no
leading/trailing spaces) are returned after validation.

---

Nitpick comments:
In `@backend/tests/test_endpoints.py`:
- Around line 67-126: The three tests test_resume_contact_round_trips,
test_resume_rejects_invalid_email_via_api, and
test_resume_rejects_invalid_github_url_via_api duplicate the same resume JSON;
replace the inline JSON in each with calls to the shared helper
make_resume_payload(...) from conftest.py (override specific fields like email
or github_url per-test as needed), update usages in those tests to pass the
returned dict to client.post(..., json=...), and remove the duplicated literal
payloads so tests DRYly reuse the single payload builder.

In `@backend/tests/test_markdown_generator.py`:
- Around line 57-71: The test test_contact_fields_omitted_when_empty calls
build_resume_markdown but asserts broadly with assert "github" not in md;
tighten this to assert the exact rendered field token is absent (e.g., the label
or markdown token your renderer emits such as "GitHub:" or the specific link
label) so it only checks that the rendered contact field is omitted; update the
assertion in test_contact_fields_omitted_when_empty to look for that exact token
instead of the substring "github".

In `@frontend/src/utils/careerDiff.test.ts`:
- Around line 17-18: Update the careerDiff.test.ts suite to explicitly assert
that changes to the new contact fields are detected and rolled back: add
assertions that a diff produced for the fixture with email and github_url
changes contains entries for "email" and "github_url" and that invoking the same
rollback helper used in this test file (the test's diff detection and rollback
functions, e.g., computeCareerDiff / applyRollback or the local helpers already
used in the file) restores the original email and github_url values; place these
checks alongside the existing diff detection/rollback tests in
careerDiff.test.ts so both detection and rollback paths for email and github_url
are covered.
🪄 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: f4f70d59-5ebb-4360-9c86-f87167c64bd1

📥 Commits

Reviewing files that changed from the base of the PR and between ef11b30 and 8e122b2.

📒 Files selected for processing (36)
  • backend/alembic_migrations/versions/0043_add_contact_to_resumes.py
  • backend/app/messages.json
  • backend/app/models/resume.py
  • backend/app/repositories/resume.py
  • backend/app/routers/resumes.py
  • backend/app/schemas/resume.py
  • backend/app/services/markdown/generators/resume_generator.py
  • backend/app/services/pdf/generators/resume_generator.py
  • backend/app/services/pdf/templates/resume.css
  • backend/tests/auth/test_endpoints.py
  • backend/tests/conftest.py
  • backend/tests/security/_helpers.py
  • backend/tests/test_endpoints.py
  • backend/tests/test_markdown_generator.py
  • backend/tests/test_pdf_generator.py
  • backend/tests/test_schemas.py
  • frontend/e2e/auth.spec.ts
  • frontend/e2e/career-dirty-indicator.spec.ts
  • frontend/e2e/career-field-modal.spec.ts
  • frontend/src/api/generated.ts
  • frontend/src/components/forms/CareerDiffModal.module.css
  • frontend/src/components/forms/CareerResumeForm.tsx
  • frontend/src/components/forms/sections/CareerBasicInfoSection.tsx
  • frontend/src/constants/messages.ts
  • frontend/src/formMappers.ts
  • frontend/src/formTypes.ts
  • frontend/src/hooks/career/useCareerDirty.test.ts
  • frontend/src/hooks/career/useCareerDirty.ts
  • frontend/src/hooks/career/useCareerExperienceMutators.test.ts
  • frontend/src/hooks/career/useResumeDiffPreview.test.ts
  • frontend/src/payloadBuilders.test.ts
  • frontend/src/payloadBuilders.ts
  • frontend/src/utils/careerDiff.test.ts
  • frontend/src/utils/careerDiff.ts
  • frontend/src/utils/careerDraft.ts
  • frontend/tests/payloadBuilders.test.cjs

Comment thread backend/app/schemas/resume.py Outdated
- careerDiff.test: email / github_url の変更検出と項目別ロールバック復元を明示テスト
- test_endpoints: 連絡先系 3 テストの重複 payload を conftest.make_resume_payload に集約
- test_markdown_generator: 空 github の省略確認を厳密なラベルトークン "**github:**" で判定
- schemas/resume: email / github_url validator が前後空白を除去した正規化値を返すよう修正

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@yusuke0610
yusuke0610 merged commit bc74b40 into main Jun 10, 2026
18 checks passed
@yusuke0610
yusuke0610 deleted the feat/resume-contact-email-github branch June 20, 2026 06:54
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