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}
-
-
-
-
-
-
-
현재 생성 가능한 문서의 필수정보를 모두 확인했습니다.
-
- 선행 단계가 남으면 검토 가능한 초안과 ‘선행 단계 필요’ 상태를 분리합니다.
-
-
-
-
-
-
-
-
- )
-}