guardia-hrm/frontend/src/pages/uiws/system/CompanyManagement.tsx

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>
)
}