Skip to content
Draft
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
4 changes: 2 additions & 2 deletions src/api/approvals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function jsonResponse(body: unknown, status = 200) {

function task(): TaskDetailResponse {
return {
task_id: 'T-1', worker_id: 'W-1', case_id: null, task_type: 'STAY_PERIOD_EXTENSION',
task_id: 'T-1', target_type: 'WORKER', worker_id: 'W-1', case_id: null, task_type: 'STAY_PERIOD_EXTENSION',
workflow_id: 'wf-stay', workflow_catalog_version: '3', title: '체류기간 연장', description: '안내',
business_data: { office: '수원' }, source: 'MANUAL', status: 'DRAFT', due_date: '2026-08-10',
content_revision: 2, version: 7, missing_required_slots: [], checklist_items: [], created_by: 'U-1',
Expand All @@ -35,7 +35,7 @@ describe('approval APIs', () => {
expected_version: 7,
ai_snapshot: null,
hr_snapshot: {
worker_id: 'W-1', task_type: 'STAY_PERIOD_EXTENSION', workflow_id: 'wf-stay',
target_type: 'WORKER', worker_id: 'W-1', task_type: 'STAY_PERIOD_EXTENSION', workflow_id: 'wf-stay',
title: '체류기간 연장', description: '안내', due_date: '2026-08-10', business_data: { office: '수원' },
},
changed_fields: ['task_content'],
Expand Down
1 change: 1 addition & 0 deletions src/api/approvals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function buildTaskApprovalSnapshot(task: TaskDetailResponse): RequestTask
expected_version: task.version,
ai_snapshot: null,
hr_snapshot: {
target_type: task.target_type,
worker_id: task.worker_id,
task_type: task.task_type,
workflow_id: task.workflow_id,
Expand Down
26 changes: 25 additions & 1 deletion src/api/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,32 @@ export type AuditAction =
| 'EXTERNAL_SUBMISSION_RECORDED'
| 'EVIDENCE_RECORDED'
| 'TASK_COMPLETED'
| 'FILE_UPLOADED'
| 'FILE_DOWNLOADED'
| 'WORKER_DOCUMENT_FILE_LINKED'
| 'DOCUMENT_REQUEST_DRAFT_SAVED'
| 'AI_RUN_CREATED'
| 'AI_RUN_ANSWERS_SUBMITTED'
| 'AI_RUN_CANDIDATES_DECIDED'
| 'OUTBOX_MANUAL_RETRY_REQUESTED'
| 'WORKER_LINK_RESPONSE_SUBMITTED'
| 'WORKER_LINK_RESPONSES_REVIEWED'
| 'WORKER_LINK_ACCESSED'
| 'USER_AGREEMENTS_RECORDED'
| 'PASSWORD_RESET_REQUESTED'
| 'PASSWORD_RESET_COMPLETED'

export type AuditTargetType = 'TASK' | 'APPROVAL_REQUEST' | 'EXTERNAL_SUBMISSION' | 'EVIDENCE'
export type AuditTargetType =
| 'TASK'
| 'APPROVAL_REQUEST'
| 'EXTERNAL_SUBMISSION'
| 'EVIDENCE'
| 'FILE'
| 'WORKER_DOCUMENT'
| 'DOCUMENT_REQUEST_DRAFT'
| 'AI_RUN'
| 'OUTBOX_EVENT'
| 'USER_ACCOUNT'

export interface AuditEventResponse {
audit_event_id: string
Expand Down
26 changes: 25 additions & 1 deletion src/api/tasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ describe('fetchTasks', () => {

await fetchTasks({
status: 'READY_FOR_REVIEW',
targetType: 'COMPANY',
source: 'SYSTEM_DDAY',
caseId: 'CASE-17',
keyword: '체류연장',
page: 1,
Expand All @@ -40,7 +42,10 @@ describe('fetchTasks', () => {

const [url] = vi.mocked(fetch).mock.calls[0]
expect(url).toContain('status=READY_FOR_REVIEW')
expect(url).toContain('caseId=CASE-17')
expect(url).toContain('target_type=COMPANY')
expect(url).toContain('source=SYSTEM_DDAY')
expect(url).toContain('case_id=CASE-17')
expect(url).not.toContain('caseId=')
expect(url).toContain('keyword=%EC%B2%B4%EB%A5%98%EC%97%B0%EC%9E%A5')
expect(url).toContain('page=1&size=20')
})
Expand Down Expand Up @@ -77,6 +82,25 @@ describe('createTask', () => {
title: '체류연장 준비',
})
})

it('supports a company task without worker_id', async () => {
vi.mocked(fetch).mockResolvedValueOnce(jsonResponse({ task_id: 'T-company' }, 201))

await createTask({
target_type: 'COMPANY',
task_type: 'PAYROLL_EXPLANATION',
workflow_id: 'wf-payroll-explanation',
title: '급여명세서 설명 준비',
})

const [, init] = vi.mocked(fetch).mock.calls[0]
expect(JSON.parse(init?.body as string)).toEqual({
target_type: 'COMPANY',
task_type: 'PAYROLL_EXPLANATION',
workflow_id: 'wf-payroll-explanation',
title: '급여명세서 설명 준비',
})
})
})

describe('updateTask', () => {
Expand Down
32 changes: 26 additions & 6 deletions src/api/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,16 @@ export type TaskStatus =
| 'COMPLETED'
| 'CANCELLED'

export type TaskType = 'RECONTRACT' | 'EMPLOYMENT_PERIOD_EXTENSION' | 'STAY_PERIOD_EXTENSION'
export type TaskType =
| 'RECONTRACT'
| 'EMPLOYMENT_PERIOD_EXTENSION'
| 'STAY_PERIOD_EXTENSION'
| 'DOCUMENT_REQUEST'
| 'WORKER_ONBOARDING'
| 'PAYROLL_EXPLANATION'
| 'EMPLOYMENT_CHANGE'
| 'WORK_INSTRUCTION'
export type TaskTargetType = 'WORKER' | 'COMPANY'
export type TaskSource = 'MANUAL' | 'SYSTEM_DDAY' | 'AI_CANDIDATE'

export interface TaskChecklistItemResponse {
Expand All @@ -27,7 +36,8 @@ export interface TaskChecklistItemResponse {

export interface TaskDetailResponse {
task_id: string
worker_id: string
target_type: TaskTargetType
worker_id: string | null
case_id: string | null
task_type: TaskType
workflow_id: string
Expand All @@ -50,7 +60,8 @@ export interface TaskDetailResponse {

export interface TaskSummaryResponse {
task_id: string
worker_id: string
target_type: TaskTargetType
worker_id: string | null
case_id: string | null
task_type: TaskType
workflow_id: string
Expand All @@ -76,6 +87,8 @@ export interface TaskPageResponse {
export interface FetchTasksParams {
status?: TaskStatus
taskType?: TaskType
targetType?: TaskTargetType
source?: TaskSource
workerId?: string
caseId?: string
dueFrom?: string
Expand All @@ -89,8 +102,10 @@ export function fetchTasks(params: FetchTasksParams = {}): Promise<TaskPageRespo
const query = new URLSearchParams()
if (params.status) query.set('status', params.status)
if (params.taskType) query.set('taskType', params.taskType)
if (params.targetType) query.set('target_type', params.targetType)
if (params.source) query.set('source', params.source)
if (params.workerId) query.set('workerId', params.workerId)
if (params.caseId) query.set('caseId', params.caseId)
if (params.caseId) query.set('case_id', params.caseId)
if (params.dueFrom) query.set('dueFrom', params.dueFrom)
if (params.dueTo) query.set('dueTo', params.dueTo)
if (params.keyword) query.set('keyword', params.keyword)
Expand All @@ -103,8 +118,7 @@ export function fetchTaskById(taskId: string): Promise<TaskDetailResponse> {
return apiFetch<TaskDetailResponse>(`/tasks/${encodeURIComponent(taskId)}`)
}

export interface CreateTaskBody {
worker_id: string
interface CreateTaskFields {
case_id?: string
task_type: TaskType
workflow_id: string
Expand All @@ -114,6 +128,12 @@ export interface CreateTaskBody {
business_data?: Record<string, unknown>
}

export type CreateTaskBody = CreateTaskFields &
(
| { target_type?: 'WORKER'; worker_id: string }
| { target_type: 'COMPANY'; worker_id?: never }
)

export function createTask(body: CreateTaskBody): Promise<TaskDetailResponse> {
return apiFetch<TaskDetailResponse>('/tasks', { method: 'POST', body: JSON.stringify(body) })
}
Expand Down
40 changes: 39 additions & 1 deletion src/api/workers.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { fetchWorkerById, fetchWorkers } from './workers'
import { fetchWorkerById, fetchWorkers, patchWorker, registerWorker } from './workers'

function jsonResponse(body: unknown) {
return new Response(JSON.stringify(body), {
Expand Down Expand Up @@ -49,3 +49,41 @@ describe('fetchWorkerById', () => {
expect(url).toContain('/workers/W-1')
})
})

describe('worker E-9 fields', () => {
it('sends visa and employment dates when registering a worker', async () => {
vi.mocked(fetch).mockResolvedValueOnce(jsonResponse({ worker_id: 'W-2' }))

await registerWorker({
display_name: '응웬반A',
visa_type: 'E-9',
employment_permit_end_date: '2028-03-01',
employment_activity_end_date: '2028-03-01',
})

const [, init] = vi.mocked(fetch).mock.calls[0]
expect(JSON.parse(init?.body as string)).toEqual({
display_name: '응웬반A',
visa_type: 'E-9',
employment_permit_end_date: '2028-03-01',
employment_activity_end_date: '2028-03-01',
})
})

it('keeps expected_version when patching employment dates', async () => {
vi.mocked(fetch).mockResolvedValueOnce(jsonResponse({ worker_id: 'W-2' }))

await patchWorker('W-2', {
employment_permit_end_date: '2028-04-01',
expected_version: 3,
})

const [url, init] = vi.mocked(fetch).mock.calls[0]
expect(url).toContain('/workers/W-2')
expect(init?.method).toBe('PATCH')
expect(JSON.parse(init?.body as string)).toEqual({
employment_permit_end_date: '2028-04-01',
expected_version: 3,
})
})
})
9 changes: 9 additions & 0 deletions src/api/workers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ export interface WorkerResponse {
nationality_code: string
preferred_language: string
work_status: WorkStatus
visa_type: string | null
stay_expiry_date: string | null
contract_start_date: string | null
contract_end_date: string | null
employment_permit_end_date: string | null
employment_activity_end_date: string | null
created_at: string
updated_at: string
version: number
Expand Down Expand Up @@ -54,9 +57,12 @@ export interface WorkerCreateBody {
display_name: string
nationality_code?: string
preferred_language?: string
visa_type?: string
stay_expiry_date?: string
contract_start_date?: string
contract_end_date?: string
employment_permit_end_date?: string
employment_activity_end_date?: string
}

export function registerWorker(body: WorkerCreateBody): Promise<WorkerResponse> {
Expand All @@ -68,9 +74,12 @@ export interface WorkerPatchBody {
nationality_code?: string
preferred_language?: string
work_status?: WorkStatus
visa_type?: string
stay_expiry_date?: string
contract_start_date?: string
contract_end_date?: string
employment_permit_end_date?: string
employment_activity_end_date?: string
expected_version: number
}

Expand Down
Loading
Loading