Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""merge resume_projects challenge/action/result into description(課題・行動・成果の統合)

プロジェクトの「課題」「行動」「成果」は分割する必要がないため、単一の自由記述欄
「詳細」(description) に統合した。これに伴い resume_projects の challenge / action /
result カラムを削除し、description カラムを新設する。

統合対象の既存本番データは存在しないため、データ移行(3 カラムの連結)は行わない。

libSQL (SQLite 互換) は ALTER COLUMN / DROP COLUMN を直接サポートしないため、
batch_alter_table(テーブル再作成)でカラムを入れ替える。

Revision ID: 0037_merge_project_caf_into_description
Revises: 0036_rename_github_analysis_to_github_link
Create Date: 2026-05-27 00:00:00.000000
"""

from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "0037_merge_project_caf_into_description"
down_revision: Union[str, None] = "0036_rename_github_analysis_to_github_link"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
with op.batch_alter_table("resume_projects") as batch_op:
batch_op.add_column(
sa.Column("description", sa.Text(), nullable=False, server_default=""),
)
batch_op.drop_column("challenge")
batch_op.drop_column("action")
batch_op.drop_column("result")


def downgrade() -> None:
with op.batch_alter_table("resume_projects") as batch_op:
batch_op.add_column(
sa.Column("challenge", sa.Text(), nullable=False, server_default=""),
)
batch_op.add_column(
sa.Column("action", sa.Text(), nullable=False, server_default=""),
)
batch_op.add_column(
sa.Column("result", sa.Text(), nullable=False, server_default=""),
)
batch_op.drop_column("description")
5 changes: 2 additions & 3 deletions backend/app/models/resume.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,8 @@ class ResumeProject(Base):
end_date_value: Mapped[date | None] = mapped_column("end_date", Date, nullable=True)
is_current: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
role: Mapped[str] = mapped_column(String(200), nullable=False, default="")
challenge: Mapped[str] = mapped_column(Text, nullable=False, default="")
action: Mapped[str] = mapped_column(Text, nullable=False, default="")
result: Mapped[str] = mapped_column(Text, nullable=False, default="")
# 課題・行動・成果を統合した自由記述欄。見出しは「詳細」。
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
team_total: Mapped[str] = mapped_column(String(60), nullable=False, default="")
team_member_rows: Mapped[list["ResumeProjectTeamMember"]] = relationship(
back_populates="project",
Expand Down
4 changes: 1 addition & 3 deletions backend/app/repositories/resume.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,7 @@ def _build_project_row(self, index: int, payload: dict[str, object]) -> ResumePr
),
is_current=payload.get("is_current", False),
role=payload.get("role", ""),
challenge=payload.get("challenge", ""),
action=payload.get("action", ""),
result=payload.get("result", ""),
description=payload.get("description", ""),
team_total=team.get("total", ""),
team_member_rows=[
ResumeProjectTeamMember(
Expand Down
5 changes: 2 additions & 3 deletions backend/app/schemas/resume.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ class Project(BaseModel):
end_date: str = Field(default="", max_length=30)
is_current: bool = False
role: str = Field(max_length=200, default="")
challenge: str = Field(max_length=1500, default="")
action: str = Field(max_length=1500, default="")
result: str = Field(max_length=1500, default="")
# 課題・行動・成果を統合した自由記述欄(見出し「詳細」)
description: str = Field(max_length=4500, default="")
team: ProjectTeam = Field(default_factory=ProjectTeam)
technology_stacks: list[TechnologyStackItem] = Field(default_factory=list)
phases: list[str] = Field(default_factory=list)
Expand Down
12 changes: 3 additions & 9 deletions backend/app/services/markdown/generators/resume_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,9 @@ def build_resume_markdown(payload: dict[str, Any]) -> str:
role = _a(proj, "role")
if role:
lines.append(field_line("担当", role))
challenge = _a(proj, "challenge")
if challenge:
lines.append(field_line("課題", challenge))
action = _a(proj, "action")
if action:
lines.append(field_line("行動", action))
result = _a(proj, "result")
if result:
lines.append(field_line("成果", result))
description = _a(proj, "description")
if description:
lines.append(field_line("詳細", description))
# 体制(後方互換: 旧 scale → team の正規化は shared に集約)
team = normalize_team(proj)
if team:
Expand Down
12 changes: 3 additions & 9 deletions backend/app/services/pdf/generators/resume_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,9 @@ def _build_project_html(project) -> str:

# 左カラム: 業務内容
left_parts: list[str] = []
challenge = _a(project, "challenge")
if challenge:
left_parts.append(f"<strong>【課題】</strong>{_md(challenge)}")
action = _a(project, "action")
if action:
left_parts.append(f"<strong>【行動】</strong>{_md(action)}")
result = _a(project, "result")
if result:
left_parts.append(f"<strong>【成果】</strong>{_md(result)}")
description = _a(project, "description")
if description:
left_parts.append(f"<strong>【詳細】</strong>{_md(description)}")
left_content = "".join(left_parts) if left_parts else "-"

# 右カラム: 開発環境(技術スタック)
Expand Down
4 changes: 1 addition & 3 deletions backend/tests/test_delete_documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@
"end_date": "2022-03",
"is_current": False,
"role": "SE",
"challenge": "",
"action": "",
"result": "",
"description": "",
"team": {"total": "5", "members": []},
"technology_stacks": [],
"phases": [],
Expand Down
4 changes: 1 addition & 3 deletions backend/tests/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,7 @@ def test_resume_round_trips_nested_structure(client: TestClient) -> None:
"end_date": "2024-03",
"is_current": False,
"role": "SE",
"challenge": "性能改善",
"action": "非同期化",
"result": "応答時間短縮",
"description": "性能改善のため非同期化し応答時間を短縮",
"team": {
"total": "5",
"members": [{"role": "SE", "count": 3}],
Expand Down
5 changes: 1 addition & 4 deletions backend/tests/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ def experience_payload() -> dict:
"end_date": "2024-03",
"is_current": False,
"role": "メンバー",
"description": "API開発",
"challenge": "課題",
"action": "行動",
"result": "処理速度を改善",
"description": "課題・行動・成果をまとめた詳細",
"team": {
"total": "5",
"members": [
Expand Down
4 changes: 1 addition & 3 deletions frontend/e2e/career-dirty-indicator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ test.describe("職務経歴書 未保存マーク", () => {
end_date: "2022-03",
is_current: false,
role: "Eng",
challenge: "",
action: "",
result: "",
description: "",
team: { total: "5", members: [] },
technology_stacks: [],
phases: [],
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/App.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
}

.sidebar {
width: 220px;
width: var(--sidebar-width);
background: var(--sidebar-bg);
color: #fff;
display: flex;
Expand Down Expand Up @@ -73,7 +73,7 @@

.mainContent {
flex: 1;
margin-left: 220px;
margin-left: var(--sidebar-width);
overflow-y: auto;
height: 100vh;
background: var(--bg-page);
Expand Down Expand Up @@ -135,6 +135,6 @@

.mainContent {
margin-left: 0;
margin-top: 48px;
margin-top: var(--topbar-height);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export function CareerExperienceEditor({
<div className={styles.inputWithUnit}>
<input
type="number"
min="0"
value={exp.employee_count}
onChange={(e) =>
onUpdateExperienceField(expIndex, "employee_count", e.target.value)
Expand All @@ -180,6 +181,7 @@ export function CareerExperienceEditor({
<div className={styles.inputWithUnit}>
<input
type="number"
min="0"
value={exp.capital}
onChange={(e) => onUpdateExperienceField(expIndex, "capital", e.target.value)}
placeholder="例: 5"
Expand Down
13 changes: 1 addition & 12 deletions frontend/src/components/forms/CareerResumeForm.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
}

.splitter:hover {
background: #b7c5e0;
background: var(--accent);
}

/* 右: PDF カラム(幅は CSS 変数 --pdf-col-width で可変。中の panel が縦に伸びる)。
Expand All @@ -140,17 +140,6 @@
padding-left: 1rem;
}

/* 最大化中: フォームとスプリッターを隠し、PDF をフォームに覆い被せて全幅表示する */
.maximized .formCol,
.maximized .splitter {
display: none;
}

.maximized .pdfCol {
flex: 1;
width: auto;
}

/* 横幅が足りない時(2 カラムを並べると最小幅を割る幅)は縦積みに切り替える。
閾値 = PDF 最小(280) + フォーム最小(360) + gap/splitter(38) ≒ 680px。
これ未満では左右に並べず、フォームの下に PDF を全幅で積む(要素が切れない)。 */
Expand Down
17 changes: 4 additions & 13 deletions frontend/src/components/forms/CareerResumeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,7 @@ export function CareerResumeForm() {
const assist = useResumeImportAssist();
const pdfInputRef = useRef<HTMLInputElement>(null);
const splitRef = useRef<HTMLDivElement>(null);
const {
width: pdfWidth,
maximized: pdfMaximized,
startResize,
toggleMaximize,
} = usePdfPanelLayout(splitRef);
const { width: pdfWidth, startResize } = usePdfPanelLayout(splitRef);
const {
form,
setForm,
Expand Down Expand Up @@ -180,14 +175,14 @@ export function CareerResumeForm() {
</div>

<div className={shared.pageBody}>
{/* 左=入力フォーム / 右=PDF 原本ビュー。スプリッターで左右リサイズ、最大化で覆い被せる
{/* 左=入力フォーム / 右=PDF 原本ビュー。スプリッターで左右リサイズする
splitWrap はコンテナクエリの基準(= split に割り当てられる実幅)。
横幅が足りない時は split を縦積みに切り替える。PDF カラム幅は CSS 変数で渡し、
縦積み時は CSS 側で全幅に上書きする(inline style だと上書きできないため変数経由)。 */}
<div className={layout.splitWrap}>
<div
ref={splitRef}
className={`${layout.split} ${pdfMaximized ? layout.maximized : ""}`}
className={layout.split}
style={{ "--pdf-col-width": `${pdfWidth}px` } as CSSProperties}
>
{/* 左: 入力フォーム(選択中フィールドは緑枠 = import-assign-form の :focus CSS) */}
Expand Down Expand Up @@ -257,11 +252,7 @@ export function CareerResumeForm() {
{/* 右: PDF 原本ビュー(独立スクロール)。文字を選択して入力欄へ流し込む。
幅は CSS 変数 --pdf-col-width を CSS 側で参照(縦積み時は全幅へ上書き)。 */}
<aside className={layout.pdfCol}>
<ResumePdfTracePanel
assist={assist}
maximized={pdfMaximized}
onToggleMaximize={toggleMaximize}
/>
<ResumePdfTracePanel assist={assist} />
</aside>
</div>
</div>
Expand Down
74 changes: 63 additions & 11 deletions frontend/src/components/forms/ProjectModal.module.css
Original file line number Diff line number Diff line change
@@ -1,40 +1,65 @@
/* サイドバーを覆わず(操作可能なまま残し)、その右のコンテンツ領域いっぱいにモーダルを開く。
overlay 自体を left:サイドバー幅 から始めるので、z-index がサイドバー(100)より上でも
サイドバーは露出したままになる。 */
.overlay {
position: fixed;
inset: 0;
left: var(--sidebar-width);
background: var(--overlay-bg);
display: flex;
align-items: center;
justify-content: center;
padding: 1.2rem;
z-index: 1000;
}

/* モーダルはコンテンツ領域いっぱいに広げる(職務経歴書ページと同等の作業領域を確保)。 */
.modal {
background: var(--bg-card);
border-radius: 12px;
width: 90vw;
max-width: 800px;
max-height: 85vh;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}

/* 取り込みパネルを併置する分だけ横幅を広げる */
.modalWide {
max-width: 1150px;
/* モバイルはサイドバーが上部バーになるため、その下から全幅で開く。 */
@media (max-width: 768px) {
.overlay {
left: 0;
top: var(--topbar-height);
padding: 0.8rem;
}
}

/* ヘッダー下の領域。左=入力フォーム / 右=取り込みブロックの 2 カラム */
/* ヘッダー下の領域。左=入力フォーム / 右=取り込みブロックの 2 カラム。
スプリッターのドラッグで左右の比率を変えられる。 */
.bodyWrap {
flex: 1;
display: flex;
min-height: 0;
overflow: hidden;
}

/* モーダル内に再掲する PDF 原本ビュー(タブ固定・ページ部のみスクロール) */
/* 入力フォームと PDF カラムの境界。ドラッグで左右比率を変える。 */
.splitter {
flex-shrink: 0;
align-self: stretch;
width: 6px;
border-radius: 3px;
background: var(--border);
cursor: col-resize;
transition: background 0.15s;
}

.splitter:hover {
background: var(--accent);
}

/* モーダル内に再掲する PDF 原本ビュー(タブ固定・ページ部のみスクロール)。
幅は CSS 変数 --pdf-col-width で可変(縦積み時は下のメディアクエリで全幅に上書き)。
inline style ではなく変数経由にすることで、縦積み時の全幅化を CSS 側で上書きできる。 */
.blocksColumn {
width: 320px;
width: var(--pdf-col-width, 320px);
flex-shrink: 0;
display: flex;
flex-direction: column;
Expand All @@ -44,6 +69,33 @@
padding: 1rem;
}

/* 横幅が狭い時は左右 2 カラムをやめ、フォームの下に PDF を全幅で縦積みする。
モーダルは width:90vw なのでビューポート幅で判定する。 */
@media (max-width: 700px) {
.bodyWrap {
flex-direction: column;
overflow-y: auto;
}

/* 縦積み時はモーダル全体スクロールに委ねる(カラム個別スクロールを解除) */
.body {
overflow-y: visible;
}

/* 縦積み時はドラッグでの左右リサイズを無効化 */
.splitter {
display: none;
}

/* 幅指定を上書きして全幅。区切りは左ボーダーから上ボーダーへ。 */
.blocksColumn {
width: 100%;
min-height: 60vh;
border-left: none;
border-top: 1px solid var(--border);
}
}

.header {
display: flex;
justify-content: space-between;
Expand Down
Loading
Loading