diff --git a/src/pages/CreateWorkPage/CreateWorkPage.test.tsx b/src/pages/CreateWorkPage/CreateWorkPage.test.tsx index 02a52bf..bd6925b 100644 --- a/src/pages/CreateWorkPage/CreateWorkPage.test.tsx +++ b/src/pages/CreateWorkPage/CreateWorkPage.test.tsx @@ -97,13 +97,12 @@ describe('CreateWorkPage', () => { }) }) - it('jumps to a later review step when clicking it in the shared step indicator', async () => { - const user = userEvent.setup() + it('renders the shared step indicator as a non-interactive progress display', () => { renderPage() - await user.click(screen.getByRole('button', { name: '3 초안 작성' })) - - expect(await screen.findByText('검토 화면?step=2')).toBeInTheDocument() + expect(screen.getByText('1 요청 확인')).toBeInTheDocument() + expect(screen.getByText('2 분석 결과')).toBeInTheDocument() + expect(screen.queryByRole('button', { name: /분석 결과/ })).not.toBeInTheDocument() }) it('opens the file import wizard from the work item panel', async () => { diff --git a/src/pages/CreateWorkPage/CreateWorkPage.tsx b/src/pages/CreateWorkPage/CreateWorkPage.tsx index 446c34e..00f96e7 100644 --- a/src/pages/CreateWorkPage/CreateWorkPage.tsx +++ b/src/pages/CreateWorkPage/CreateWorkPage.tsx @@ -58,11 +58,6 @@ export function CreateWorkPage() { } } - function handleStepClick(index: number) { - if (index === 0) return - navigate(`/tasks/new/review?step=${index}`) - } - return (
@@ -71,7 +66,7 @@ export function CreateWorkPage() {
- +
diff --git a/src/pages/ReviewWorkPage/ReviewWorkPage.test.tsx b/src/pages/ReviewWorkPage/ReviewWorkPage.test.tsx index 390dd91..260c7cc 100644 --- a/src/pages/ReviewWorkPage/ReviewWorkPage.test.tsx +++ b/src/pages/ReviewWorkPage/ReviewWorkPage.test.tsx @@ -1,11 +1,8 @@ -import { render, screen, within } from '@testing-library/react' -import userEvent from '@testing-library/user-event' +import { render, screen } from '@testing-library/react' import { MemoryRouter, Route, Routes } from 'react-router-dom' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { ToastViewport } from '../../components/ui/ToastViewport/ToastViewport' import { useToastStore } from '../../store/toastStore' import { ReviewWorkPage } from './ReviewWorkPage' -import { DRAFT_DOCUMENTS, HR_VERIFICATION_FIELDS, REVIEW_STEPS, STRUCTURED_FIELDS } from './reviewWorkData' import type { AiRunResponse } from '../../api/aiRuns' beforeEach(() => { @@ -24,17 +21,10 @@ function renderPage(path = '/tasks/new/review', state?: unknown) { 업무함

} /> 업무 생성 페이지

} /> - , ) } -async function fillVerificationFields(user: ReturnType) { - for (const field of HR_VERIFICATION_FIELDS) { - await user.type(screen.getByLabelText(field.label), '2027-01-01') - } -} - describe('ReviewWorkPage', () => { it('uses the server AiRun result when an actual analysis is provided', () => { const aiRun: AiRunResponse = { @@ -67,84 +57,9 @@ describe('ReviewWorkPage', () => { expect(screen.getByLabelText('신청 목표일을 입력해 주세요. *')).toBeInTheDocument() }) - it('renders every step of the shared progress indicator', () => { - renderPage() - const indicator = screen.getByRole('list', { name: '진행 단계' }) - expect(within(indicator).getAllByRole('listitem')).toHaveLength(REVIEW_STEPS.length) - }) - - it('renders the information pending table with editable HR fields', () => { - renderPage() - - expect(screen.getByText('누락정보를 해결 주체별로 확인해 주세요')).toBeInTheDocument() - for (const field of HR_VERIFICATION_FIELDS) { - expect(screen.getByLabelText(field.label)).toBeInTheDocument() - } - }) - - it('shows a toast when saving a temporary draft', async () => { - const user = userEvent.setup() - renderPage() - - await user.click(screen.getByRole('button', { name: '임시 저장' })) - - expect(screen.getByText('임시 저장했습니다.')).toBeInTheDocument() - }) - - it('disables the draft generation button until every HR verification field is filled', async () => { - const user = userEvent.setup() - renderPage() - - const generate = screen.getByRole('button', { name: '초안 생성' }) - expect(generate).toBeDisabled() - - await fillVerificationFields(user) - - expect(generate).toBeEnabled() - }) - - it('advances through draft preparation to final review and can navigate to the task list', async () => { - const user = userEvent.setup() - renderPage() - await fillVerificationFields(user) - await user.click(screen.getByRole('button', { name: '초안 생성' })) - - expect(await screen.findByText('생성된 문서와 대기 중인 문서를 확인해 주세요')).toBeInTheDocument() - for (const doc of DRAFT_DOCUMENTS) { - expect(screen.getByText(doc.title)).toBeInTheDocument() - } - - const draftReviewButtons = screen.getAllByRole('button', { name: '초안 검토' }) - await user.click(draftReviewButtons[draftReviewButtons.length - 1]) - - expect(await screen.findByText('문서 검토본과 입력값을 최종 확인해 주세요')).toBeInTheDocument() - for (const field of STRUCTURED_FIELDS) { - expect(screen.getAllByText(field.label).length).toBeGreaterThan(0) - } - - await user.click(screen.getByRole('button', { name: '승인 요청' })) - - expect(await screen.findByRole('heading', { name: /승인이 완료됐습니다./ })).toBeInTheDocument() - - await user.click(screen.getByRole('button', { name: '업무함으로 이동 →' })) - - expect(await screen.findByText('업무함')).toBeInTheDocument() - }) - - it('opens directly on a given step via the ?step= query param', () => { - renderPage('/tasks/new/review?step=3') - - expect(screen.getByText('문서 검토본과 입력값을 최종 확인해 주세요')).toBeInTheDocument() - }) - - it('jumps freely between steps by clicking the shared indicator', async () => { - const user = userEvent.setup() - renderPage('/tasks/new/review?step=3') - - await user.click(screen.getByRole('button', { name: '✓ 정보 보완' })) - expect(screen.getByText('누락정보를 해결 주체별로 확인해 주세요')).toBeInTheDocument() + it('redirects to the request input screen when there is no aiRunId to review', async () => { + renderPage('/tasks/new/review') - await user.click(screen.getByRole('button', { name: '✓ 요청 확인' })) expect(await screen.findByText('업무 생성 페이지')).toBeInTheDocument() }) }) diff --git a/src/pages/ReviewWorkPage/ReviewWorkPage.tsx b/src/pages/ReviewWorkPage/ReviewWorkPage.tsx index c3b31c0..da0af74 100644 --- a/src/pages/ReviewWorkPage/ReviewWorkPage.tsx +++ b/src/pages/ReviewWorkPage/ReviewWorkPage.tsx @@ -2,32 +2,18 @@ import { useEffect, useState } from 'react' import { Link, useLocation, useNavigate, useSearchParams } from 'react-router-dom' import { ApiError, getErrorMessage } from '../../api/errors' import { fetchAiRun, type AiRunResponse } from '../../api/aiRuns' -import { WorkflowStepIndicator } from '../../components/ui/WorkflowStepIndicator/WorkflowStepIndicator' import { readAiRunWorkRequestDraft, type WorkRequestDraft, } from '../CreateWorkPage/workRequestDraft' import { AiRunReview } from './AiRunReview' import styles from './ReviewWorkPage.module.css' -import { REVIEW_STEPS } from './reviewWorkData' -import { DraftPreparationStep } from './steps/DraftPreparationStep' -import { FinalReviewStep } from './steps/FinalReviewStep' -import { InformationPendingStep } from './steps/InformationPendingStep' - -// REVIEW_STEPS 인덱스 기준 2~4단계(정보 보완/초안 준비/최종 검토) 내부 위저드. -// 1.요청 확인은 CreateWorkPage(/tasks/new)에서 진행되고, 같은 WorkflowStepIndicator를 공유한다. -type WizardStepIndex = 1 | 2 | 3 interface ReviewLocationState { aiRun?: AiRunResponse draft?: WorkRequestDraft } -function parseStepIndex(value: string | null): WizardStepIndex { - const parsed = Number(value) - return parsed === 2 || parsed === 3 ? parsed : 1 -} - export function ReviewWorkPage() { const navigate = useNavigate() const location = useLocation() @@ -38,7 +24,6 @@ export function ReviewWorkPage() { const [recoveredRun, setRecoveredRun] = useState(null) const [recovering, setRecovering] = useState(Boolean(aiRunId && !navigationRun)) const [recoveryError, setRecoveryError] = useState(null) - const [stepIndex, setStepIndex] = useState(() => parseStepIndex(searchParams.get('step'))) const aiRun = navigationRun ?? recoveredRun const draft = navigationState?.draft ?? (aiRunId ? readAiRunWorkRequestDraft(aiRunId) : null) @@ -68,13 +53,10 @@ export function ReviewWorkPage() { } }, [aiRunId, navigationRun]) - function handleStepClick(index: number) { - if (index === 0) { - navigate('/tasks/new') - return - } - setStepIndex(index as WizardStepIndex) - } + // 분석 실행 번호(aiRunId) 없이 이 화면에 직접 들어오면 검토할 내용이 없다 — 요청 입력으로 되돌린다. + useEffect(() => { + if (!aiRunId && !navigationRun) navigate('/tasks/new', { replace: true }) + }, [aiRunId, navigationRun, navigate]) if (aiRun) { return @@ -104,19 +86,5 @@ export function ReviewWorkPage() { ) } - return ( -
-
- - ← 업무 생성 - -
- - - - {stepIndex === 1 && setStepIndex(2)} />} - {stepIndex === 2 && setStepIndex(3)} />} - {stepIndex === 3 && } -
- ) + return null } diff --git a/src/pages/ReviewWorkPage/reviewWorkData.ts b/src/pages/ReviewWorkPage/reviewWorkData.ts index e1865aa..4cc241f 100644 --- a/src/pages/ReviewWorkPage/reviewWorkData.ts +++ b/src/pages/ReviewWorkPage/reviewWorkData.ts @@ -1,238 +1,10 @@ -// TODO(backend): 이 파일 전체는 Figma PWF3/Screen/REVIEW-001/01~04 목업의 목데이터다. -// GET /api/work-items/draft?requestId= 등 실제 API 연동 시 아래 목데이터를 대체한다. - export interface ReviewStep { no: string label: string } -// Figma REVIEW-001(PWF3/Screen/REVIEW-001/01~04) 4단계 진행 표시기. 01 요청 확인은 -// CreateWorkPage(/tasks/new)에서 보여주고, 02~04는 ReviewWorkPage 내부 위저드에서 보여준다 -// — 두 페이지가 이 배열을 함께 쓴다. +// CreateWorkPage(1.요청 확인)와 ReviewWorkPage(AI Run 분석 결과) 진행 표시기가 함께 쓰는 단계 라벨. export const REVIEW_STEPS: ReviewStep[] = [ { no: '1', label: '요청 확인' }, - { no: '2', label: '정보 보완' }, - { no: '3', label: '초안 작성' }, - { no: '4', label: '최종 검토' }, -] - -export type PillTone = 'neutral' | 'brand' | 'green' | 'amber' | 'red' - -// === 02 정보 보완 === - -export interface ResolutionRow { - owner: string - ownerTone: PillTone - field: string - blocked: string - method: string - state: string - stateTone: PillTone -} - -export const RESOLUTION_ROWS: ResolutionRow[] = [ - { - owner: '근로자 요청', - ownerTone: 'brand', - field: '여권 사본 · 만료일', - blocked: '차단 해제', - method: '응우옌 반 A · 보안 링크', - state: '반영 완료', - stateTone: 'brand', - }, - { - owner: '선행 업무 결과', - ownerTone: 'neutral', - field: '서명된 근로계약서', - blocked: '후속 차단', - method: '재계약 조건 확인 결과 연결', - state: '선행 대기', - stateTone: 'amber', - }, -] - -// HR이 직접 입력해야 하는 정보 — Agent가 대신 판단하지 않는다. -export interface HrVerificationField { - key: string - label: string -} - -export const HR_VERIFICATION_FIELDS: HrVerificationField[] = [ - { key: 'passportExpiry', label: '여권 유효기간' }, - { key: 'permitPeriod', label: '고용허가기간' }, - { key: 'activityPeriod', label: '취업활동기간' }, -] - -export const RESOLUTION_MATRIX_META = '생성 대상 필수정보 9/9 · 다음 처리 절차의 선행정보 1건 대기' -export const RESOLUTION_MATRIX_FOOTNOTE = - '근로자 응답은 후보값이며 HR이 반영하기 전에는 기본정보와 초안에 확정되지 않습니다.' - -export const SECURE_LINK = { - status: '완료', - title: '응우옌 반 A · 여권 사본 요청', - meta: 'LINK-2026-081 · 68시간 남음', - note: '재발급 시 기존 링크 자동 폐기', -} - -export const WORKER_CANDIDATE = { - sourceLabel: '근로자 제출', - fieldLabel: '여권 만료일', - value: '2026.08.07', - note: '여권 사본과 입력값을 함께 확인', - reflectedBy: '김민지 HR · 오늘 10:24 반영', -} - -export const GENERATION_GATE = { - readyCount: 2, - blockedCount: 1, - note: '필수정보가 남아 있으면 초안을 생성할 수 없습니다.', -} - -// === 03 초안 작성 === - -export type DraftDocumentStatus = 'ready' | 'blocked' | 'failed' - -export interface DraftDocument { - title: string - meta: string - status: DraftDocumentStatus - statusLabel: string - actionLabel: string -} - -export const DRAFT_DOCUMENTS: DraftDocument[] = [ - { - title: '표준근로계약서', - meta: 'PDF 검토본 준비 · 원본 형식 HWPX', - status: 'ready', - statusLabel: '초안 대기', - actionLabel: '초안 검토', - }, - { - title: '근로자 서류 요청 안내문', - meta: '쉬운 한국어 안내 · 제출기한 8월 7일', - status: 'ready', - statusLabel: '초안 대기', - actionLabel: '초안 검토', - }, - { - title: '취업활동기간 연장신청서', - meta: '서명된 계약서 등록 후 생성', - status: 'blocked', - statusLabel: '선행 대기', - actionLabel: '조건 보기 →', - }, - { - title: '고용센터 제출 체크리스트', - meta: '공식 서식 버전 확인 실패', - status: 'failed', - statusLabel: '생성 실패', - actionLabel: '다시 생성', - }, + { no: '2', label: '분석 결과' }, ] - -export const LEAVE_WARNING = { - title: '화면 이탈 안내', - body: '생성 중인 항목이 있으면 결과를 저장한 뒤 이동합니다. 실패한 문서는 입력값을 유지한 채 다시 생성할 수 있습니다.', -} - -export const SOURCE_COUNTS = [ - { label: '기존 등록 정보', value: '18개' }, - { label: 'HR 입력', value: '4개' }, - { label: '근로자 제출', value: '1개' }, -] - -export const DOCUMENT_STATE_LEGEND = [ - '초안 검토 대기 · PDF 준비', - '선행 단계 필요 · 조건 미충족', - '생성 실패 · 입력값 유지 후 재시도', -] - -export const NEXT_WORKFLOW_GATE = { - title: '체류기간 연장 준비', - status: '선행 대기', - note: '서명된 근로계약서가 문서함에 등록되면 체류기간 연장 준비가 열립니다.', -} - -export const GENERATION_BOUNDARY_NOTE = 'Agent는 문서를 생성·변환하지만 승인과 외부 제출은 수행하지 않습니다.' - -// === 04 최종 검토 === - -export const DOCUMENT_TABS = ['표준근로계약서', '서류 요청 안내문', '연장신청서 · 선행 필요'] - -export const PDF_PREVIEW = { - title: '표준근로계약서', - subtitle: '고용노동부 표준서식 · 검토본', - rows: [ - { label: '근로자 성명', value: '응우옌 반 A' }, - { label: '계약 기간', value: '2026.10.01 — 2027.09.29', highlighted: true }, - { label: '근무 장소', value: 'FOWOCO 데모 사업장' }, - { label: '담당 업무', value: '제조 · 조립 라인' }, - { label: '임금 지급일', value: '매월 10일' }, - ], - footer: '검토용 PDF · 승인 전', - disclaimer: '원본 HWP/HWPX는 보존되며, 이 화면은 서버 변환 PDF 검토본입니다.', -} - -export const TEMPLATE_METADATA = { - title: '표준근로계약서', - meta: '공식 표준근로계약서 · 버전 2026.1 · 최종 확인 2026.07.30', - roleLabel: '작성자', -} - -export interface StructuredField { - label: string - value: string - source: string - sourceTone: PillTone - note: string - noteTone: PillTone -} - -export const STRUCTURED_FIELDS: StructuredField[] = [ - { - label: '근로자 성명', - value: '응우옌 반 A', - source: '기존 등록 정보', - sourceTone: 'neutral', - note: '핵심값 보존 완료', - noteTone: 'green', - }, - { - label: '계약 종료일', - value: '2026.09.30 → 2027.09.29', - source: 'HR 입력', - sourceTone: 'brand', - note: '변경 1건', - noteTone: 'brand', - }, - { - label: '여권 만료일', - value: '2026.08.07', - source: '근로자 제출', - sourceTone: 'amber', - note: '유효기간 경고', - noteTone: 'neutral', - }, - { - label: '근무 장소', - value: '기존 DB 값 유지', - source: '기존 등록 정보', - sourceTone: 'neutral', - note: '핵심값 보존 완료', - noteTone: 'green', - }, -] - -export const VALIDATION_SUMMARY = { - summary: '핵심값 보존 7/7 · 오류 0 · 경고 1', - note: '여권 만료일이 제출기한과 가까워 최종 확인이 필요합니다.', - backLinkLabel: '누락 · 충돌 발생 시 정보 보완으로 돌아가기 →', -} - -export const APPROVAL_SUMMARY = { - approver: '김민지', - pendingTitle: '작성자 검토가 완료되면 승인 요청을 보낼 수 있습니다.', - pendingNote: '승인권자는 같은 위치에서 ‘승인하고 문서함에 저장’을 실행합니다.', - approvedNote: '업무함에서 진행 상황을 확인할 수 있습니다.', -} diff --git a/src/pages/ReviewWorkPage/steps/DraftPreparationStep.tsx b/src/pages/ReviewWorkPage/steps/DraftPreparationStep.tsx deleted file mode 100644 index f841c84..0000000 --- a/src/pages/ReviewWorkPage/steps/DraftPreparationStep.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { useNavigate } from 'react-router-dom' -import { Button } from '../../../components/ui/Button/Button' -import styles from '../ReviewWorkPage.module.css' -import { - DOCUMENT_STATE_LEGEND, - DRAFT_DOCUMENTS, - GENERATION_BOUNDARY_NOTE, - LEAVE_WARNING, - NEXT_WORKFLOW_GATE, - SOURCE_COUNTS, - type DraftDocumentStatus, -} from '../reviewWorkData' - -const STATUS_PILL_CLASS: Record = { - ready: 'pillGreen', - blocked: 'pillAmber', - failed: 'pillRed', -} - -export interface DraftPreparationStepProps { - onDone: () => void -} - -export function DraftPreparationStep({ onDone }: DraftPreparationStepProps) { - const navigate = useNavigate() - - return ( -
-
-
-

생성된 문서와 대기 중인 문서를 확인해 주세요

-

선행조건이 충족된 문서만 준비했습니다. 완료 후에도 자동 이동하지 않습니다.

-
- 초안 대기 -
- -
-
-

문서 · 안내문 생성 결과

-

문서별 상태와 다음 행동

- -
- {DRAFT_DOCUMENTS.map((doc) => ( -
-
-

{doc.title}

-

{doc.meta}

-
-
- - {doc.statusLabel} - - -
-
- ))} -
- -
-

{LEAVE_WARNING.title}

-

{LEAVE_WARNING.body}

-
-
- -
-
-

초안에 사용한 값

- {SOURCE_COUNTS.map((item) => ( -
- {item.label} - {item.value} -
- ))} -
- -
-

문서 상태 기준

-
- {DOCUMENT_STATE_LEGEND.map((line) => ( -

{line}

- ))} -
-
- -
-

다음 처리 절차 조건

-
- {NEXT_WORKFLOW_GATE.title} - {NEXT_WORKFLOW_GATE.status} -
-

{NEXT_WORKFLOW_GATE.note}

-
- -
-

{GENERATION_BOUNDARY_NOTE}

-
-
-
- -
-
-

검토 가능 2건 · 선행 필요 1건

-

완료되어도 자동 이동하지 않습니다. HR이 ‘초안 검토’를 선택합니다.

-
-
- - -
-
-
- ) -} diff --git a/src/pages/ReviewWorkPage/steps/FinalReviewStep.tsx b/src/pages/ReviewWorkPage/steps/FinalReviewStep.tsx deleted file mode 100644 index 454b042..0000000 --- a/src/pages/ReviewWorkPage/steps/FinalReviewStep.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { useState } from 'react' -import { useNavigate } from 'react-router-dom' -import { Button } from '../../../components/ui/Button/Button' -import styles from '../ReviewWorkPage.module.css' -import { - APPROVAL_SUMMARY, - DOCUMENT_TABS, - PDF_PREVIEW, - STRUCTURED_FIELDS, - TEMPLATE_METADATA, - VALIDATION_SUMMARY, - type PillTone, -} from '../reviewWorkData' - -const SOURCE_PILL_CLASS: Record = { - neutral: 'pillNeutral', - brand: 'pillBrand', - green: 'pillGreen', - amber: 'pillAmber', - red: 'pillRed', -} - -const NOTE_CLASS: Record = { - neutral: 'fieldReviewNoteNeutral', - brand: 'fieldReviewNoteBrand', - green: 'fieldReviewNoteGreen', - amber: 'fieldReviewNoteNeutral', - red: 'fieldReviewNoteNeutral', -} - -export function FinalReviewStep() { - const navigate = useNavigate() - const [approved, setApproved] = useState(false) - - function handleApprove() { - // TODO(backend): POST /api/work-items/approve -> 승인권자 최종 승인 처리 - setApproved(true) - } - - if (approved) { - return ( -
-
-
-

- {' '} - 승인이 완료됐습니다. -

-

{APPROVAL_SUMMARY.approvedNote}

-
- 승인 완료 -
- -
-

{TEMPLATE_METADATA.title}

-
- 승인자 - {APPROVAL_SUMMARY.approver} -
-
- -
-
-

{APPROVAL_SUMMARY.approvedNote}

-
-
- -
-
-
- ) - } - - return ( -
-
-
-

문서 검토본과 입력값을 최종 확인해 주세요

-

PDF 검토본은 직접 편집하지 않습니다. 오른쪽 구조화 필드를 수정한 뒤 다시 생성합니다.

-
- 최종 검토 -
- -
-
-
- {DOCUMENT_TABS.map((tab, index) => ( - - {tab} - - ))} -
- -
- PDF 검토본 - 1 / 3 - 맞춤 100% -
- -
-
-

{PDF_PREVIEW.title}

-

{PDF_PREVIEW.subtitle}

- {PDF_PREVIEW.rows.map((row) => ( -
- {row.label} - {row.value} -
- ))} -

{PDF_PREVIEW.footer}

-
-
- -

{PDF_PREVIEW.disclaimer}

-
- -
-
-
-

{TEMPLATE_METADATA.title}

- {TEMPLATE_METADATA.roleLabel} -
-

{TEMPLATE_METADATA.meta}

-
- -
- 변경 필드만 - 오류만 - 전체 필드 -
- -
- {STRUCTURED_FIELDS.map((field) => ( -
-
-
-

{field.label}

-

{field.value}

-
- - {field.source} - -
-

{field.note}

-
- ))} -
- -
-
-

검증 결과

- {VALIDATION_SUMMARY.summary} -
-

{VALIDATION_SUMMARY.note}

- -
-
-
- -
-
-

{APPROVAL_SUMMARY.pendingTitle}

-

{APPROVAL_SUMMARY.pendingNote}

-
-
- - -
-
-
- ) -} diff --git a/src/pages/ReviewWorkPage/steps/InformationPendingStep.tsx b/src/pages/ReviewWorkPage/steps/InformationPendingStep.tsx deleted file mode 100644 index 4aa0a6f..0000000 --- a/src/pages/ReviewWorkPage/steps/InformationPendingStep.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import { useState } from 'react' -import { Button } from '../../../components/ui/Button/Button' -import { useToastStore } from '../../../store/toastStore' -import styles from '../ReviewWorkPage.module.css' -import { - GENERATION_GATE, - HR_VERIFICATION_FIELDS, - RESOLUTION_MATRIX_FOOTNOTE, - RESOLUTION_MATRIX_META, - RESOLUTION_ROWS, - SECURE_LINK, - WORKER_CANDIDATE, -} from '../reviewWorkData' - -const OWNER_TONE_CLASS: Record = { - brand: styles.tableOwner, - neutral: `${styles.tableOwner} ${styles.tableOwnerNeutral}`, -} - -const STATE_TONE_CLASS: Record = { - brand: styles.pillBrand, - neutral: styles.pillNeutral, - green: styles.pillGreen, - amber: styles.pillAmber, - red: styles.pillRed, -} - -export interface InformationPendingStepProps { - onComplete: () => void -} - -export function InformationPendingStep({ onComplete }: InformationPendingStepProps) { - const showToast = useToastStore((state) => state.showToast) - const [verification, setVerification] = useState>(() => - Object.fromEntries(HR_VERIFICATION_FIELDS.map((field) => [field.key, ''])), - ) - - const canComplete = HR_VERIFICATION_FIELDS.every((field) => verification[field.key]?.trim() !== '') - - function handleVerificationChange(key: string, value: string) { - setVerification((prev) => ({ ...prev, [key]: value })) - } - - function handleSaveTemp() { - // TODO(backend): PATCH /api/work-items/draft -> 현재 입력 상태 저장 - showToast('임시 저장했습니다.') - } - - return ( -
-
-
-

누락정보를 해결 주체별로 확인해 주세요

-

- 필수정보의 담당자·수집 방법·차단 여부를 확인한 뒤 생성 가능한 초안만 준비합니다. -

-
- 생성 가능 -
- -
-
-

해결할 정보

-

{RESOLUTION_MATRIX_META}

- -
-
- 해결 주체 · 정보 - 차단 여부 - 담당 · 수집 방법 - 현재 상태 -
- - {HR_VERIFICATION_FIELDS.map((field) => { - const filled = verification[field.key]?.trim() !== '' - return ( -
-
- HR 직접 입력 - {field.label} -
- 비차단 - handleVerificationChange(field.key, event.target.value)} - /> - - {filled ? '선택 완료' : '입력 필요'} - -
- ) - })} - - {RESOLUTION_ROWS.map((row) => ( -
-
- {row.owner} - {row.field} -
- {row.blocked} - {row.method} - {row.state} -
- ))} -
- -

{RESOLUTION_MATRIX_FOOTNOTE}

-
- -
-
-
-

근로자 보안 링크

- {SECURE_LINK.status} -
-

{SECURE_LINK.title}

-

{SECURE_LINK.meta}

-

{SECURE_LINK.note}

-
- - -
-
- -
-
-

근로자 응답 후보

- {WORKER_CANDIDATE.sourceLabel} -
-
- {WORKER_CANDIDATE.fieldLabel} - {WORKER_CANDIDATE.value} -
-

{WORKER_CANDIDATE.note}

-

{WORKER_CANDIDATE.reflectedBy}

-
- -
-

생성 가능 범위

-
- 초안 대기 - {GENERATION_GATE.readyCount}건 -
-
- 선행 단계가 필요한 문서 - {GENERATION_GATE.blockedCount}건 -
-

{GENERATION_GATE.note}

-
-
-
- -
-
-

현재 생성 가능한 문서의 필수정보를 모두 확인했습니다.

-

- 선행 단계가 남으면 검토 가능한 초안과 ‘선행 단계 필요’ 상태를 분리합니다. -

-
-
- - -
-
-
- ) -}