291 lines
11 KiB
TypeScript
291 lines
11 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
import { Button, Checkbox, DatePicker, Input, Select, message } from 'antd'
|
|
import dayjs from 'dayjs'
|
|
import Modal from './DraggableModal'
|
|
import { FormField, FormGrid, FormSection } from './OrderFormLayout'
|
|
import client, { apiPrefix, unwrap, type ApiResponse } from '../api/client'
|
|
import { options } from '../pages/care/homesShared'
|
|
import { statusOptions, PROGRESS_STATUS, GIFT_STATUS, GIFT_PLACE } from '../pages/care/receptionStatuses'
|
|
|
|
type Props = {
|
|
open: boolean
|
|
selectedIds: number[]
|
|
progressStatuses: string[]
|
|
giftStatuses: string[]
|
|
centerOptions?: string[]
|
|
onCancel: () => void
|
|
onSuccess: () => void
|
|
}
|
|
|
|
const GIFT_PLACES = Object.entries(GIFT_PLACE).map(([value, label]) => ({ value, label }))
|
|
|
|
export default function ReceptionBulkModal({
|
|
open,
|
|
selectedIds,
|
|
progressStatuses,
|
|
giftStatuses,
|
|
centerOptions = [],
|
|
onCancel,
|
|
onSuccess,
|
|
}: Props) {
|
|
const [busy, setBusy] = useState(false)
|
|
|
|
const [pgAdminOnly, setPgAdminOnly] = useState(false)
|
|
const [pgStatus, setPgStatus] = useState<string | undefined>()
|
|
const [pgNotNotice, setPgNotNotice] = useState(false)
|
|
const [pgCenterId, setPgCenterId] = useState<string | undefined>()
|
|
const [pgMemo, setPgMemo] = useState('')
|
|
const [pgOpended, setPgOpended] = useState('')
|
|
const [pgCanceled, setPgCanceled] = useState('')
|
|
|
|
const [gPlace, setGPlace] = useState<string | undefined>()
|
|
const [gStatus, setGStatus] = useState<string | undefined>()
|
|
const [gProgressLog, setGProgressLog] = useState('')
|
|
const [gDeliveryCompany, setGDeliveryCompany] = useState('')
|
|
const [gTrackingNumber, setGTrackingNumber] = useState('')
|
|
const [gRequested, setGRequested] = useState('')
|
|
const [gSended, setGSended] = useState('')
|
|
|
|
useEffect(() => {
|
|
if (!open) return
|
|
setPgAdminOnly(false)
|
|
setPgStatus(undefined)
|
|
setPgNotNotice(false)
|
|
setPgCenterId(undefined)
|
|
setPgMemo('')
|
|
setPgOpended('')
|
|
setPgCanceled('')
|
|
setGPlace(undefined)
|
|
setGStatus(undefined)
|
|
setGProgressLog('')
|
|
setGDeliveryCompany('')
|
|
setGTrackingNumber('')
|
|
setGRequested('')
|
|
setGSended('')
|
|
}, [open])
|
|
|
|
const need = () => {
|
|
if (!selectedIds.length) {
|
|
message.warning('대상 접수를 선택하세요.')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
const run = async (fn: () => Promise<void>) => {
|
|
setBusy(true)
|
|
try {
|
|
await fn()
|
|
onSuccess()
|
|
} catch (e: unknown) {
|
|
message.error(e instanceof Error ? e.message : '일괄 처리에 실패했습니다.')
|
|
} finally {
|
|
setBusy(false)
|
|
}
|
|
}
|
|
|
|
const runProgress = () => {
|
|
if (!need()) return
|
|
if (!pgStatus) {
|
|
message.warning('진행상황을 선택하세요.')
|
|
return
|
|
}
|
|
if (!window.confirm(`선택 ${selectedIds.length}건을 일괄 처리하시겠습니까?`)) return
|
|
void run(async () => {
|
|
const { data } = await client.post<ApiResponse<{ count: number }>>(`${apiPrefix()}/homes/receptions/bulk-progress`, {
|
|
ids: selectedIds,
|
|
status: pgStatus,
|
|
memo: pgMemo,
|
|
centerId: pgCenterId || null,
|
|
opended: pgOpended || null,
|
|
canceled: pgCanceled || null,
|
|
adminOnly: pgAdminOnly,
|
|
notNotice: pgNotNotice,
|
|
})
|
|
const res = unwrap(data) as { count: number }
|
|
message.success(`${res.count}건 진행상황을 변경했습니다.`)
|
|
})
|
|
}
|
|
|
|
const runGift = () => {
|
|
if (!need()) return
|
|
if (!gStatus) {
|
|
message.warning('사은품진행상황을 선택하세요.')
|
|
return
|
|
}
|
|
if (!window.confirm(`선택 ${selectedIds.length}건을 일괄 처리하시겠습니까?`)) return
|
|
void run(async () => {
|
|
const { data } = await client.post<ApiResponse<{ count: number }>>(`${apiPrefix()}/homes/receptions/bulk-gift`, {
|
|
ids: selectedIds,
|
|
place: gPlace || null,
|
|
status: gStatus,
|
|
giftProgressLog: gProgressLog,
|
|
deliveryCompany: gDeliveryCompany || null,
|
|
trackingNumber: gTrackingNumber || null,
|
|
requested: gRequested || null,
|
|
sended: gSended || null,
|
|
})
|
|
const res = unwrap(data) as { count: number }
|
|
message.success(`${res.count}건 사은품진행을 변경했습니다.`)
|
|
})
|
|
}
|
|
|
|
const runCenterWait = () => {
|
|
if (!need()) return
|
|
if (!window.confirm('입력처 정산대기로 일괄처리 하시겠습니까?')) return
|
|
void run(async () => {
|
|
const { data } = await client.post<ApiResponse<{ count: number }>>(`${apiPrefix()}/homes/receptions/bulk-center-progress`, {
|
|
ids: selectedIds,
|
|
})
|
|
const res = unwrap(data) as { count: number }
|
|
message.success(`${res.count}건을 입력처 정산대기로 변경했습니다.`)
|
|
})
|
|
}
|
|
|
|
return (
|
|
<Modal
|
|
title={`일괄진행 (${selectedIds.length}건 선택)`}
|
|
open={open}
|
|
onCancel={onCancel}
|
|
footer={null}
|
|
destroyOnHidden
|
|
width="96%"
|
|
style={{ maxWidth: 1040 }}
|
|
className="reception-bulk-modal reception-apply-modal order-form-modal"
|
|
styles={{ body: { maxHeight: 'calc(92vh - 56px)', overflowY: 'auto' } }}
|
|
>
|
|
<div className="order-form-embedded-inner order-form-page reception-apply-form reception-bulk-form">
|
|
<FormSection title="진행상황 일괄 처리">
|
|
<FormGrid>
|
|
<FormField label="관리자 전용로그" full>
|
|
<div className="order-form-inline">
|
|
<Checkbox checked={pgAdminOnly} onChange={(e) => setPgAdminOnly(e.target.checked)}>
|
|
관리자 전용로그
|
|
</Checkbox>
|
|
<span className="bulk-hint">■ 관리자만 보이고 영업점은 보이지 않습니다.</span>
|
|
</div>
|
|
</FormField>
|
|
<FormField label="진행상황" full>
|
|
<div className="order-form-inline">
|
|
<Select
|
|
allowClear
|
|
placeholder="진행상황 선택"
|
|
value={pgStatus}
|
|
onChange={(v) => setPgStatus(v)}
|
|
options={statusOptions(
|
|
Object.fromEntries((progressStatuses.length ? progressStatuses : Object.keys(PROGRESS_STATUS)).map((c) => [c, PROGRESS_STATUS[c] || c])),
|
|
)}
|
|
style={{ flex: 1, minWidth: 180 }}
|
|
/>
|
|
<Checkbox checked={pgNotNotice} onChange={(e) => setPgNotNotice(e.target.checked)}>
|
|
하부점에 전달하지 않음
|
|
</Checkbox>
|
|
</div>
|
|
<p className="bulk-hint-line">■ 퀵전송으로 등록된 접수인 경우 진행상황을 하부점에 알리지 않습니다.</p>
|
|
<p className="bulk-hint-line">■ 진행상황을 1개 선택후 검색하여야 진행상황을 변경할 수 있습니다.</p>
|
|
</FormField>
|
|
<FormField label="입력처" full>
|
|
<Select
|
|
allowClear
|
|
placeholder="입력처 선택"
|
|
value={pgCenterId}
|
|
onChange={(v) => setPgCenterId(v)}
|
|
options={options(centerOptions.length ? centerOptions : ['본사'])}
|
|
/>
|
|
</FormField>
|
|
<FormField label="업무진행메모(LOG)" full>
|
|
<Input.TextArea rows={3} value={pgMemo} onChange={(e) => setPgMemo(e.target.value)} />
|
|
</FormField>
|
|
<FormField label="개통일자">
|
|
<DatePicker
|
|
value={pgOpended ? dayjs(pgOpended) : null}
|
|
onChange={(d) => setPgOpended(d ? d.format('YYYY-MM-DD') : '')}
|
|
style={{ width: '100%' }}
|
|
/>
|
|
</FormField>
|
|
<FormField label="해지일자">
|
|
<DatePicker
|
|
value={pgCanceled ? dayjs(pgCanceled) : null}
|
|
onChange={(d) => setPgCanceled(d ? d.format('YYYY-MM-DD') : '')}
|
|
style={{ width: '100%' }}
|
|
/>
|
|
</FormField>
|
|
</FormGrid>
|
|
<div className="bulk-foot">
|
|
<button type="button" className="bulk-submit" disabled={busy} onClick={runProgress}>선택 일괄 처리</button>
|
|
</div>
|
|
</FormSection>
|
|
|
|
<FormSection title="본사발송 사은품 일괄 처리">
|
|
<FormGrid>
|
|
<FormField label="사은품 지급처">
|
|
<Select
|
|
allowClear
|
|
placeholder="지급처 선택"
|
|
value={gPlace}
|
|
onChange={(v) => setGPlace(v)}
|
|
options={GIFT_PLACES}
|
|
/>
|
|
<p className="bulk-hint-line">■ 지급처를 검색하세요.</p>
|
|
</FormField>
|
|
<FormField label="사은품진행상황">
|
|
<Select
|
|
allowClear
|
|
placeholder="사은품 진행상황 선택"
|
|
value={gStatus}
|
|
onChange={(v) => setGStatus(v)}
|
|
options={statusOptions(
|
|
Object.fromEntries((giftStatuses.length ? giftStatuses : Object.keys(GIFT_STATUS)).map((c) => [c, GIFT_STATUS[c] || c])),
|
|
)}
|
|
/>
|
|
</FormField>
|
|
<FormField label="사은품진행메모(LOG)" full>
|
|
<Input.TextArea rows={3} value={gProgressLog} onChange={(e) => setGProgressLog(e.target.value)} />
|
|
</FormField>
|
|
<FormField label="택배회사">
|
|
<Input value={gDeliveryCompany} onChange={(e) => setGDeliveryCompany(e.target.value)} />
|
|
</FormField>
|
|
<FormField label="송장번호">
|
|
<Input value={gTrackingNumber} onChange={(e) => setGTrackingNumber(e.target.value)} />
|
|
</FormField>
|
|
<FormField label="사은품지급요청일">
|
|
<DatePicker
|
|
value={gRequested ? dayjs(gRequested) : null}
|
|
onChange={(d) => setGRequested(d ? d.format('YYYY-MM-DD') : '')}
|
|
style={{ width: '100%' }}
|
|
/>
|
|
</FormField>
|
|
<FormField label="사은품지급완료일">
|
|
<DatePicker
|
|
value={gSended ? dayjs(gSended) : null}
|
|
onChange={(d) => setGSended(d ? d.format('YYYY-MM-DD') : '')}
|
|
style={{ width: '100%' }}
|
|
/>
|
|
</FormField>
|
|
</FormGrid>
|
|
<div className="bulk-foot">
|
|
<button type="button" className="bulk-submit" disabled={busy} onClick={runGift}>선택 일괄 처리</button>
|
|
</div>
|
|
</FormSection>
|
|
|
|
<FormSection title="입력처 - 정산대기로 일괄처리">
|
|
<FormGrid cols={1}>
|
|
<FormField label="안내" full>
|
|
<p className="bulk-desc-block" style={{ padding: 0, margin: 0 }}>
|
|
■ 개통완료된 고객중에 입력처 정산이 진행안된 고객은 입력처 정산대기로 일괄처리 진행할수 있습니다.
|
|
</p>
|
|
</FormField>
|
|
</FormGrid>
|
|
<div className="bulk-foot">
|
|
<button type="button" className="bulk-submit" disabled={busy} onClick={runCenterWait}>선택 일괄 처리</button>
|
|
</div>
|
|
</FormSection>
|
|
</div>
|
|
|
|
<div className="balance-modal-footer" style={{ marginTop: 12 }}>
|
|
<Button onClick={onCancel}>닫기</Button>
|
|
</div>
|
|
</Modal>
|
|
)
|
|
}
|