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
24 changes: 20 additions & 4 deletions frontend/src/app/router/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBrowserRouter } from 'react-router-dom'
import { createBrowserRouter, Navigate } from 'react-router-dom'
import { RequireAuth } from '@/features/auth'
import HomePage from '@/pages/Home'
import LoginPage from '@/pages/Login'
Expand All @@ -7,7 +7,6 @@ import WorkspacePage from '@/pages/Workspace'
import InterviewSetupPage from '@/pages/InterviewSetup'
import InterviewSessionPage from '@/pages/InterviewSession'
import SessionFeedbackPage from '@/pages/SessionFeedback'
import HistoryPage from '@/pages/History'
import SharedFeedbackPage from '@/pages/SharedFeedback'

export const router = createBrowserRouter([
Expand All @@ -23,6 +22,22 @@ export const router = createBrowserRouter([
</RequireAuth>
),
},
{
path: '/workspace/resumes',
element: (
<RequireAuth>
<WorkspacePage />
</RequireAuth>
),
},
{
path: '/workspace/repos',
element: (
<RequireAuth>
<WorkspacePage />
</RequireAuth>
),
},
{
path: '/sessions/new',
element: (
Expand All @@ -48,13 +63,14 @@ export const router = createBrowserRouter([
),
},
{
path: '/history',
path: '/workspace/history',
element: (
<RequireAuth>
<HistoryPage />
<WorkspacePage />
</RequireAuth>
),
},
{ path: '/history', element: <Navigate to="/workspace/history" replace /> },
{
path: '/design-system/*',
lazy: async () => {
Expand Down
16 changes: 12 additions & 4 deletions frontend/src/features/analysis/ui/DocumentList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ const SOURCE_LABEL: Record<AnalysisSourceType, string> = {

type Props = {
filter?: DocumentFilter
// 클라이언트에서 소스 유형으로 한정 (탭별 분석 결과 분리용).
sourceType?: AnalysisSourceType
}

export function DocumentList({ filter = {} }: Props) {
export function DocumentList({ filter = {}, sourceType }: Props) {
const { data = [], isPending, isError, error } = useDocuments(filter)

if (isPending) {
Expand All @@ -39,20 +41,26 @@ export function DocumentList({ filter = {} }: Props) {
</p>
)
}
if (data.length === 0) {

const docs = sourceType
? data.filter((doc) => doc.sourceType === sourceType)
: data

if (docs.length === 0) {
const subject = sourceType ? SOURCE_LABEL[sourceType] : '이력서·레포'
return (
<div className="rounded-xl border border-dashed border-border-strong bg-surface p-10 text-center">
<p className="text-body text-fg-muted">아직 분석된 문서가 없습니다.</p>
<p className="text-caption text-fg-subtle mt-2">
이력서·레포 분석이 완료되면 요약과 기술 스택이 여기에 표시됩니다.
{subject} 분석이 완료되면 요약과 기술 스택이 여기에 표시됩니다.
</p>
</div>
)
}

return (
<ul className="flex flex-col gap-3">
{data.map((doc) => (
{docs.map((doc) => (
<DocumentRow key={doc.id} doc={doc} />
))}
</ul>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pages/Workspace/
| `Workspace` | `/workspace` | `features/resume`, `features/repo` (도입 예정) |
| `Interview` | `/sessions/new`, `/sessions/:id` | `features/interview` |
| `Interview (Feedback)` | `/sessions/:id/feedback` | `features/feedback` |
| `History` | `/history`, `/history/:id` | `features/history` (도입 예정) |
| `History` | `/workspace/history` (구 `/history` → 리다이렉트) | `features/history` |

---

Expand Down
1 change: 0 additions & 1 deletion frontend/src/pages/History/index.ts

This file was deleted.

34 changes: 0 additions & 34 deletions frontend/src/pages/History/ui/HistoryPage.tsx

This file was deleted.

26 changes: 26 additions & 0 deletions frontend/src/pages/Workspace/ui/HistoryView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
ScoreTrend,
SessionHistoryList,
StatsSummary,
useUserStats,
} from '@/features/history'

export function HistoryView() {
const { data: stats } = useUserStats()

return (
<div className="space-y-8">
{stats && (
<div className="grid gap-4 md:grid-cols-2">
<StatsSummary stats={stats} />
<ScoreTrend stats={stats} />
</div>
)}

<section className="flex flex-col gap-3">
<h2 className="text-h6 text-fg">지난 면접</h2>
<SessionHistoryList />
</section>
</div>
)
}
103 changes: 103 additions & 0 deletions frontend/src/pages/Workspace/ui/HomeView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Link } from 'react-router-dom'
import {
ScoreTrend,
StatsSummary,
useUserStats,
} from '@/features/history'
import { Button } from '@/shared/ui/Button'

export function HomeView() {
const { data: stats } = useUserStats()
const hasSessions = (stats?.totalSessionCount ?? 0) > 0

return (
<div className="space-y-8">
<section className="overflow-hidden rounded-2xl border border-border bg-sage-600 p-8 text-white">
<p className="text-caption uppercase tracking-[0.08em] text-white/60">
맞춤 모의 면접
</p>
<h2 className="mt-2 font-heading text-h4 font-bold">
이력서·레포 기반 맞춤 면접을 시작하세요
</h2>
<p className="mt-2 max-w-xl text-body text-white/70">
면접 모드와 직군을 고르면 AI가 질문을 생성하고, 실시간으로 답변을
주고받습니다.
</p>
<div className="mt-6 flex flex-wrap gap-3">
<Link to="/sessions/new">
<Button size="lg" variant="secondary">
새 면접 시작
</Button>
</Link>
<Link to="/workspace/resumes">
<Button
size="lg"
variant="ghost"
className="text-white hover:bg-white/10"
>
자료 준비하기
</Button>
</Link>
</div>
</section>

<div className="grid gap-4 md:grid-cols-3">
<QuickLink
to="/workspace/resumes"
title="이력서"
description="이력서를 업로드하고 분석 결과를 확인하세요."
/>
<QuickLink
to="/workspace/repos"
title="레포지토리"
description="GitHub 레포를 등록하고 분석 결과를 확인하세요."
/>
<QuickLink
to="/workspace/history"
title="면접 히스토리"
description="지난 면접 기록과 점수 추이를 확인하세요."
/>
</div>

{hasSessions && stats && (
<section className="space-y-3">
<h2 className="text-h6 text-fg">한눈에 보기</h2>
<div className="grid gap-4 md:grid-cols-2">
<StatsSummary stats={stats} />
<ScoreTrend stats={stats} />
</div>
</section>
)}
</div>
)
}

function QuickLink({
to,
title,
description,
}: {
to: string
title: string
description: string
}) {
return (
<Link
to={to}
className="group flex items-center justify-between gap-4 rounded-xl border border-border bg-surface-raised p-5 transition-colors duration-fast hover:border-border-strong hover:bg-surface"
>
<div>
<h3 className="font-heading text-h6 font-bold text-fg-strong">
{title}
</h3>
<p className="mt-1 text-caption text-fg-muted">{description}</p>
</div>
<span
aria-hidden
className="text-fg-muted transition-transform duration-fast group-hover:translate-x-0.5"
>
</span>
</Link>
)
}
26 changes: 26 additions & 0 deletions frontend/src/pages/Workspace/ui/ReposView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { WorkspaceSection } from '@/widgets/workspace-section'
import { RepoList, RepoPicker } from '@/features/repo'
import { DocumentList } from '@/features/analysis'

export function ReposView() {
return (
<div className="space-y-10">
<WorkspaceSection
title="내 GitHub 레포지토리"
description="등록한 레포를 기반으로 코드 맥락에 맞는 질문이 생성됩니다."
>
<div className="space-y-4">
<RepoPicker />
<RepoList />
</div>
</WorkspaceSection>

<WorkspaceSection
title="레포지토리 분석 결과"
description="등록한 레포의 요약과 추출된 기술 스택입니다."
>
<DocumentList sourceType="REPOSITORY" />
</WorkspaceSection>
</div>
)
}
24 changes: 24 additions & 0 deletions frontend/src/pages/Workspace/ui/ResumesView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { WorkspaceSection } from '@/widgets/workspace-section'
import { ResumeList, ResumeUploader } from '@/features/resume'
import { DocumentList } from '@/features/analysis'

export function ResumesView() {
return (
<div className="space-y-10">
<WorkspaceSection
title="내 이력서"
description="PDF 이력서를 업로드하면 AI가 분석해 면접 질문 풀에 반영합니다."
action={<ResumeUploader />}
>
<ResumeList />
</WorkspaceSection>

<WorkspaceSection
title="이력서 분석 결과"
description="업로드한 이력서의 요약과 추출된 기술 스택입니다."
>
<DocumentList sourceType="RESUME" />
</WorkspaceSection>
</div>
)
}
Loading