154 lines
5.7 KiB
TypeScript
154 lines
5.7 KiB
TypeScript
|
|
import { useState } from 'react';
|
||
|
|
import { Alert, Descriptions, Form, Input, InputNumber, Modal, Space, message } from 'antd';
|
||
|
|
import { ProCard } from '@ant-design/pro-components';
|
||
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||
|
|
import type { ColDef } from '@ag-grid-community/core';
|
||
|
|
import dayjs from 'dayjs';
|
||
|
|
import PageContainer from '@/components/common/PageContainer';
|
||
|
|
import DataGrid from '@/components/common/DataGrid';
|
||
|
|
import PermissionButton from '@/components/common/PermissionButton';
|
||
|
|
import { commissionApi, StatuteConfigRow } from '@/api/commission';
|
||
|
|
import { GRAY, RADIUS, SHADOW } from '@/theme/tokens';
|
||
|
|
|
||
|
|
const COLS: ColDef<StatuteConfigRow>[] = [
|
||
|
|
{ field: 'entityType', headerName: '대상유형', width: 200 },
|
||
|
|
{ field: 'expirationMonths', headerName: '시효(개월)', width: 130, type: 'numericColumn' },
|
||
|
|
{ field: 'isActive', headerName: '활성', width: 80 },
|
||
|
|
];
|
||
|
|
|
||
|
|
export default function StatuteConfig() {
|
||
|
|
const qc = useQueryClient();
|
||
|
|
const [editTarget, setEditTarget] = useState<StatuteConfigRow | null>(null);
|
||
|
|
const [expireMonth, setExpireMonth] = useState(dayjs().format('YYYYMM'));
|
||
|
|
const [expireResult, setExpireResult] = useState<{ expiredCount: number } | null>(null);
|
||
|
|
const [form] = Form.useForm<Partial<StatuteConfigRow>>();
|
||
|
|
|
||
|
|
const { data: configData, isLoading, isError } = useQuery({
|
||
|
|
queryKey: ['statuteConfig', 'list'],
|
||
|
|
queryFn: () => commissionApi.statuteConfigs(),
|
||
|
|
retry: false,
|
||
|
|
});
|
||
|
|
|
||
|
|
const rows = configData?.list ?? [];
|
||
|
|
|
||
|
|
const updateMutation = useMutation({
|
||
|
|
mutationFn: ({ configId, values }: { configId: number; values: Partial<StatuteConfigRow> }) =>
|
||
|
|
commissionApi.statuteConfigUpdate(configId, values),
|
||
|
|
onSuccess: () => {
|
||
|
|
message.success('설정 수정 완료');
|
||
|
|
qc.invalidateQueries({ queryKey: ['statuteConfig', 'list'] });
|
||
|
|
setEditTarget(null);
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const expireMutation = useMutation({
|
||
|
|
mutationFn: (baseMonth: string) => commissionApi.statuteExpire(baseMonth),
|
||
|
|
onSuccess: (result) => {
|
||
|
|
setExpireResult(result);
|
||
|
|
message.success(`만료 처리 완료 — ${result.expiredCount}건`);
|
||
|
|
qc.invalidateQueries({ queryKey: ['statuteConfig', 'list'] });
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const handleUpdate = async () => {
|
||
|
|
const values = await form.validateFields();
|
||
|
|
updateMutation.mutate({ configId: editTarget!.configId, values });
|
||
|
|
};
|
||
|
|
|
||
|
|
const openEdit = (row: StatuteConfigRow) => {
|
||
|
|
setEditTarget(row);
|
||
|
|
form.setFieldsValue({
|
||
|
|
expirationMonths: row.expirationMonths,
|
||
|
|
isActive: row.isActive,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleExpire = () => Modal.confirm({
|
||
|
|
title: '만료 실행',
|
||
|
|
content: `기준월 ${expireMonth} 기준으로 시효 만료된 채권을 WRITTEN_OFF 처리합니다. 계속하시겠습니까?`,
|
||
|
|
okType: 'danger',
|
||
|
|
onOk: () => expireMutation.mutateAsync(expireMonth),
|
||
|
|
});
|
||
|
|
|
||
|
|
const cardStyle = { background: '#fff', borderRadius: RADIUS.lg, border: `1px solid ${GRAY[100]}`, boxShadow: SHADOW.sm };
|
||
|
|
|
||
|
|
return (
|
||
|
|
<PageContainer title="환수시효관리" description="환수채권 시효 설정 및 만료 실행">
|
||
|
|
{isError && (
|
||
|
|
<Alert type="warning" showIcon message="API 미응답" description="/api/statute 를 확인하세요." style={{ marginBottom: 16 }} />
|
||
|
|
)}
|
||
|
|
|
||
|
|
<ProCard style={{ ...cardStyle, marginBottom: 16 }} title="시효 설정">
|
||
|
|
<DataGrid<StatuteConfigRow>
|
||
|
|
rows={rows}
|
||
|
|
columns={[
|
||
|
|
...COLS,
|
||
|
|
{
|
||
|
|
headerName: '액션', width: 100, pinned: 'right',
|
||
|
|
cellRenderer: (p: { data: StatuteConfigRow }) => (
|
||
|
|
<PermissionButton
|
||
|
|
menuCode="STATUTE_CONFIG" permCode="UPDATE" size="small"
|
||
|
|
onClick={() => openEdit(p.data)}
|
||
|
|
>
|
||
|
|
수정
|
||
|
|
</PermissionButton>
|
||
|
|
),
|
||
|
|
},
|
||
|
|
]}
|
||
|
|
loading={isLoading}
|
||
|
|
height={200}
|
||
|
|
rowKey="configId"
|
||
|
|
/>
|
||
|
|
</ProCard>
|
||
|
|
|
||
|
|
<ProCard style={cardStyle} title="만료 실행">
|
||
|
|
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||
|
|
<Space>
|
||
|
|
<span>기준월:</span>
|
||
|
|
<Input
|
||
|
|
value={expireMonth}
|
||
|
|
onChange={(e) => setExpireMonth(e.target.value)}
|
||
|
|
placeholder="YYYYMM"
|
||
|
|
style={{ width: 120 }}
|
||
|
|
/>
|
||
|
|
<PermissionButton
|
||
|
|
menuCode="STATUTE_CONFIG" permCode="UPDATE"
|
||
|
|
type="primary" danger
|
||
|
|
loading={expireMutation.isPending}
|
||
|
|
onClick={handleExpire}
|
||
|
|
>
|
||
|
|
만료 실행
|
||
|
|
</PermissionButton>
|
||
|
|
</Space>
|
||
|
|
{expireResult && (
|
||
|
|
<Descriptions size="small" bordered>
|
||
|
|
<Descriptions.Item label="처리 결과">
|
||
|
|
{expireResult.expiredCount}건이 WRITTEN_OFF 처리되었습니다.
|
||
|
|
</Descriptions.Item>
|
||
|
|
</Descriptions>
|
||
|
|
)}
|
||
|
|
</Space>
|
||
|
|
</ProCard>
|
||
|
|
|
||
|
|
<Modal
|
||
|
|
title="시효 설정 수정"
|
||
|
|
open={!!editTarget}
|
||
|
|
onOk={handleUpdate}
|
||
|
|
onCancel={() => setEditTarget(null)}
|
||
|
|
okText="저장"
|
||
|
|
cancelText="취소"
|
||
|
|
confirmLoading={updateMutation.isPending}
|
||
|
|
>
|
||
|
|
<Form form={form} layout="vertical">
|
||
|
|
<Form.Item name="expirationMonths" label="시효 개월수" rules={[{ required: true }]}>
|
||
|
|
<InputNumber style={{ width: '100%' }} min={1} max={120} />
|
||
|
|
</Form.Item>
|
||
|
|
<Form.Item name="isActive" label="활성(Y/N)" rules={[{ required: true }]}>
|
||
|
|
<Input placeholder="Y 또는 N" maxLength={1} />
|
||
|
|
</Form.Item>
|
||
|
|
</Form>
|
||
|
|
</Modal>
|
||
|
|
</PageContainer>
|
||
|
|
);
|
||
|
|
}
|