107 lines
5.1 KiB
TypeScript
107 lines
5.1 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
import { companyList, createCompany, updateCompany, deleteCompany } from '../../../api/uiws'
|
|
import {
|
|
PageHeader, SearchBar, Input, Button, DataGrid, Modal, FormField, Spinner, YnBadge, type Column,
|
|
} from '../../../components/uiws/ui'
|
|
|
|
interface Company extends Record<string, unknown> {
|
|
companyId: string; companyNm: string; bizNo: string | null; useYn: string
|
|
}
|
|
|
|
function friendlyError(e: any): string {
|
|
if (e?.response?.status === 403) return '권한이 없습니다 (관리자 전용).'
|
|
if (e?.response?.status === 409) return '이미 존재하는 거래처ID입니다.'
|
|
return e?.response?.data?.message || '요청을 처리하지 못했습니다.'
|
|
}
|
|
|
|
export default function CompanyManagement() {
|
|
const [rows, setRows] = useState<Company[]>([])
|
|
const [keyword, setKeyword] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
const [err, setErr] = useState('')
|
|
const [form, setForm] = useState<{ open: boolean; edit: Company | null }>({ open: false, edit: null })
|
|
|
|
const load = async (kw = '') => {
|
|
setLoading(true); setErr('')
|
|
try { const res = await companyList(kw || undefined); setRows(res.data.data?.content ?? []) }
|
|
catch (e) { setErr(friendlyError(e)) }
|
|
finally { setLoading(false) }
|
|
}
|
|
useEffect(() => { load('') }, [])
|
|
|
|
const remove = async (c: Company) => {
|
|
if (!window.confirm(`거래처 '${c.companyNm}' 를 삭제하시겠습니까?`)) return
|
|
try { await deleteCompany(c.companyId); await load() } catch (e) { setErr(friendlyError(e)) }
|
|
}
|
|
|
|
const columns: Column<Company>[] = [
|
|
{ key: 'companyId', header: '거래처ID', width: 180 },
|
|
{ key: 'companyNm', header: '거래처명' },
|
|
{ key: 'bizNo', header: '사업자번호', width: 180, render: r => r.bizNo ?? '-' },
|
|
{ key: 'useYn', header: '사용', width: 80, align: 'center', render: r => <YnBadge yn={r.useYn} yes="사용" no="미사용" /> },
|
|
{ key: 'act', header: '', width: 60, align: 'center', render: r => <Button variant="ghost" onClick={() => remove(r)}>삭제</Button> },
|
|
]
|
|
|
|
return (
|
|
<div className="uiws-scope">
|
|
<PageHeader title="거래처 관리" subtitle="시스템관리(권한) · 거래처(근무처) 등록/수정/삭제"
|
|
actions={<Button onClick={() => setForm({ open: true, edit: null })}>+ 거래처 등록</Button>} />
|
|
|
|
{err && <div style={{ color: 'var(--uiws-danger)', fontSize: 13, marginBottom: 12 }}>{err}</div>}
|
|
|
|
<SearchBar>
|
|
<Input value={keyword} onChange={setKeyword} placeholder="거래처명 검색" style={{ minWidth: 220 }} />
|
|
<Button variant="ghost" onClick={() => load(keyword)}>검색</Button>
|
|
</SearchBar>
|
|
|
|
{loading ? <Spinner /> : (
|
|
<DataGrid columns={columns} rows={rows} rowKey={r => r.companyId}
|
|
onRowClick={r => setForm({ open: true, edit: r })} empty="등록된 거래처가 없습니다." />
|
|
)}
|
|
|
|
{form.open && (
|
|
<CompanyForm company={form.edit} onClose={() => setForm({ open: false, edit: null })}
|
|
onSaved={() => { setForm({ open: false, edit: null }); load(keyword) }} />
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function CompanyForm({ company, onClose, onSaved }: { company: Company | null; onClose: () => void; onSaved: () => void }) {
|
|
const editing = !!company
|
|
const [companyId, setCompanyId] = useState(company?.companyId ?? '')
|
|
const [companyNm, setCompanyNm] = useState(company?.companyNm ?? '')
|
|
const [bizNo, setBizNo] = useState(company?.bizNo ?? '')
|
|
const [useYn, setUseYn] = useState(company?.useYn ?? 'Y')
|
|
const [err, setErr] = useState('')
|
|
|
|
const save = async () => {
|
|
setErr('')
|
|
if (!editing && !companyId.trim()) { setErr('거래처ID는 필수입니다.'); return }
|
|
if (!companyNm.trim()) { setErr('거래처명은 필수입니다.'); return }
|
|
try {
|
|
const body = { companyId, companyNm, bizNo: bizNo || null, useYn }
|
|
editing ? await updateCompany(companyId, body) : await createCompany(body)
|
|
onSaved()
|
|
} catch (e) { setErr(friendlyError(e)) }
|
|
}
|
|
|
|
return (
|
|
<Modal title={editing ? '거래처 수정' : '거래처 등록'} onClose={onClose}
|
|
footer={<><Button variant="ghost" onClick={onClose}>취소</Button><Button onClick={save}>저장</Button></>}>
|
|
<FormField label="거래처ID"><Input value={companyId} onChange={setCompanyId} style={{ width: '100%', opacity: editing ? 0.6 : 1 }} /></FormField>
|
|
<FormField label="거래처명"><Input value={companyNm} onChange={setCompanyNm} style={{ width: '100%' }} /></FormField>
|
|
<FormField label="사업자번호"><Input value={bizNo} onChange={setBizNo} style={{ width: '100%' }} /></FormField>
|
|
<FormField label="사용 여부">
|
|
<select value={useYn} onChange={e => setUseYn(e.target.value)}
|
|
style={{ width: '100%', padding: '9px 12px', borderRadius: 8, fontSize: 13,
|
|
background: 'var(--uiws-input-bg)', border: '1px solid var(--uiws-border)', color: 'var(--uiws-text)' }}>
|
|
<option value="Y">사용</option>
|
|
<option value="N">미사용</option>
|
|
</select>
|
|
</FormField>
|
|
{err && <div style={{ color: 'var(--uiws-danger)', fontSize: 13 }}>{err}</div>}
|
|
</Modal>
|
|
)
|
|
}
|