부가서비스

- 계좌점유인증 상세정보,필터,엑셀다운 API 연동
- 계좌성명조회 상세정보 페이지 수정
This commit is contained in:
HyeonJongKim
2025-09-23 10:49:23 +09:00
parent 7d473cdc0a
commit 532d280bd9
12 changed files with 430 additions and 32 deletions

View File

@@ -0,0 +1,52 @@
import axios from 'axios';
import { API_URL_ADDITIONAL_SERVICE } from '@/shared/api/api-url-additional-service';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import {
DetailResponse,
TitleInfo,
DetailInfo,
ExtensionAccountHolderAuthDetailParams,
ExtensionAccountHolderAuthDetailResponse
} from '../../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const extensionAccountHolderAuthDetail = async (params: ExtensionAccountHolderAuthDetailParams): Promise<DetailResponse> => {
const response = await resultify(
axios.post<ExtensionAccountHolderAuthDetailResponse>(API_URL_ADDITIONAL_SERVICE.extensionAccountHolderAuthDetail(), params),
);
const detailResponse: DetailResponse = {
titleInfo: {
accountName: response.accountName,
accountNo: response.accountNo,
scheduledSendDate: response.requestDate //추후 발송예정일자로 수정
} as TitleInfo,
detailInfo: {
companyName: response.companyName,
mid: response.mid,
requestDate: response.requestDate,
bankName: response.bankName,
accountNo: response.accountNo,
accountName: response.accountName,
transferStatus: response.transferStatus,
failureReason: response.failReason,
} as DetailInfo
};
return detailResponse;
};
export const useExtensionAccountHolderAuthDetailMutation = (options?: UseMutationOptions<DetailResponse, CBDCAxiosError, ExtensionAccountHolderAuthDetailParams>) => {
const mutation = useMutation<DetailResponse, CBDCAxiosError, ExtensionAccountHolderAuthDetailParams>({
...options,
mutationFn: (params: ExtensionAccountHolderAuthDetailParams) => extensionAccountHolderAuthDetail(params),
});
return {
...mutation,
};
}

View File

@@ -0,0 +1,26 @@
import axios from 'axios';
import { API_URL_ADDITIONAL_SERVICE } from '@/shared/api/api-url-additional-service';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import { ExtensionAccountHolderAuthDownloadExcelParams, ExtensionAccountHolderAuthDownloadExcelResponse } from '../../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const extensionAccountHolderAuthDownloadExcel = (params: ExtensionAccountHolderAuthDownloadExcelParams) => {
return resultify(
axios.post<ExtensionAccountHolderAuthDownloadExcelResponse>(API_URL_ADDITIONAL_SERVICE.extensionAccountHolderAuthDownlaodExcel(), params),
);
};
export const useExtensionAccountHolderAuthDownloadExcelMutation = (options?: UseMutationOptions<ExtensionAccountHolderAuthDownloadExcelResponse, CBDCAxiosError, ExtensionAccountHolderAuthDownloadExcelParams>) => {
const mutation = useMutation<ExtensionAccountHolderAuthDownloadExcelResponse, CBDCAxiosError, ExtensionAccountHolderAuthDownloadExcelParams>({
...options,
mutationFn: (params: ExtensionAccountHolderAuthDownloadExcelParams) => extensionAccountHolderAuthDownloadExcel(params),
});
return {
...mutation,
};
};

View File

@@ -10,7 +10,7 @@ import {
export const extensionAccountHolderSearchDownloadExcel = (params: ExtensionAccountHolderSearchDownloadExcelParams) => {
return resultify(
axios.post<ExtensionAccountHolderSearchDownloadExcelResponse>(API_URL_ADDITIONAL_SERVICE.extensionKeyinDownloadExcel(), params),
axios.post<ExtensionAccountHolderSearchDownloadExcelResponse>(API_URL_ADDITIONAL_SERVICE.extensionAccountHolderSearchDownloadExcel(), params),
);
};

View File

@@ -43,6 +43,7 @@ export interface TitleInfo {
amount?: number,
corpName?: string,
accountNo?: string,
accountName?: string,
bankName?: string,
requestDate?: string,
sendDate?: string,
@@ -58,10 +59,13 @@ export interface DetailInfo {
accountNo?: string; // 계좌번호
requestWay?: string; //요청 구분
mid?: string;
companyName?: string;
email: string;
phoneNumber: string;
goodsName: string;
moid: string;
transferStatus: AuthAndTransferStatus;
}
@@ -160,6 +164,17 @@ export interface AccountHolderAuthListProps {
listItems: Record<string, Array<ListItemProps>>;
mid: string;
}
export interface AccountHolderAuthFilterProps extends FilterProps {
mid: string;
startDate: string;
endDate: string;
authStatus: AuthAndTransferStatus;
setMid: (mid: string) => void;
setStartDate: (startDate: string) => void;
setEndDate: (endDate: string) => void;
setAuthStatus: (authStatus: AuthAndTransferStatus) => void;
}
// ========================================
// 계좌성명 조회 관련 타입들
// ========================================
@@ -365,9 +380,9 @@ export interface SettlementAgencyBottomAgreeProps {
export interface ListItemProps extends
KeyInPaymentListItem, AccountHolderSearchListItem,
AccountHolderAuthListItem,LinkPaymentHistoryListItem,
LinkPaymentWaitListItem, PayoutContent,
FundAccountTransferContentItem {
AccountHolderAuthListItem, LinkPaymentHistoryListItem,
LinkPaymentWaitListItem,
PayoutContent {
additionalServiceCategory?: AdditionalServiceCategory;
mid?: string
}
@@ -564,6 +579,33 @@ export interface ExtensionAccountHolderAuthListResponse extends DefaulResponsePa
content: Array<ListItemProps>
}
export interface ExtensionAccountHolderAuthDownloadExcelParams extends ExtensionRequestParams {
mid: string;
fromDate: string;
toDate: string;
authStatus: AuthAndTransferStatus;
}
export interface ExtensionAccountHolderAuthDownloadExcelResponse {
status: boolean;
}
export interface ExtensionAccountHolderAuthDetailParams extends ExtensionRequestParams {
tid: string;
}
export interface ExtensionAccountHolderAuthDetailResponse {
tid: string;
mid: string;
accountName: string;
accountNo: string;
requestDate: string;
companyName: string;
bankName: string;
transferStatus: AuthAndTransferStatus;
failReason: string;
}
// 계좌 성명 조회 확장 서비스
// ========================================
export interface ExtensionAccountHolderSearchListParams extends ExtensionRequestParams { // Request
@@ -600,7 +642,7 @@ export interface ExtensionAccountHolderSearchRequestParams extends ExtensionRequ
}
export interface ExtensionAccountHolderSearchRequestResponse {
status : boolean;
status: boolean;
}
export interface ExtensionAccountHolderSearchDownloadExcelParams extends ExtensionRequestParams { // Request

View File

@@ -0,0 +1,114 @@
import moment from 'moment';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { motion } from 'framer-motion';
import { useState } from 'react';
import { FilterSelect } from '@/shared/ui/filter/select';
import { FilterCalendar } from '@/shared/ui/filter/calendar';
import { FilterButtonGroups } from '@/shared/ui/filter/button-groups';
import { AccountHolderAuthFilterProps, AuthAndTransferStatus } from '@/entities/additional-service/model/types';
import { authStatusBtnGroup } from '@/entities/additional-service/model/account-holder-auth/constant';
export const AccountHolderAuthFilter = ({
filterOn,
setFilterOn,
mid,
startDate,
endDate,
authStatus,
setMid,
setStartDate,
setEndDate,
setAuthStatus
}: AccountHolderAuthFilterProps) => {
const [filterMid, setFilterMid] = useState<string>(mid);
const [filterStartDate, setFilterStartDate] = useState<string>(moment(startDate).format('YYYY.MM.DD'));
const [filterEndDate, setFilterEndDate] = useState<string>(moment(endDate).format('YYYY.MM.DD'));
const [filterAuthStatus, setFilterAuthStatus] = useState<AuthAndTransferStatus>(authStatus);
const variants = {
hidden: { x: '100%' },
visible: { x: '0%' },
};
let MidOptions = [
{ name: 'nictest001m', value: 'nictest001m' },
{ name: 'nictest002m', value: 'nictest002m' }
];
const onClickToClose = () => {
setFilterOn(false);
};
const onClickToSetFilter = () => {
setMid(filterMid);
setStartDate(filterStartDate);
setEndDate(filterEndDate);
setAuthStatus(filterAuthStatus);
onClickToClose();
};
return (
<>
<motion.div
id="fullMenuModal"
className="full-menu-modal"
initial="hidden"
animate={(filterOn) ? 'visible' : 'hidden'}
variants={variants}
transition={{ duration: 0.3 }}
style={{
width: '100%',
height: '100%',
}}
>
<div className="full-menu-container">
<div className="full-menu-header">
<div className="full-menu-title center"></div>
<div className="full-menu-actions">
<button
id="closeFullMenu"
className="full-menu-close"
>
<img
src={IMAGE_ROOT + '/ico_close.svg'}
alt="닫기"
onClick={() => onClickToClose()}
/>
</button>
</div>
</div>
<div className="option-list pt-16">
<FilterSelect
title='가맹점'
selectValue={mid}
selectSetter={setMid}
selectOptions={MidOptions}
></FilterSelect>
<FilterCalendar
startDate={filterStartDate}
endDate={filterEndDate}
setStartDate={setFilterStartDate}
setEndDate={setFilterEndDate}
></FilterCalendar>
<FilterButtonGroups
title='지급상태'
activeValue={filterAuthStatus}
btnGroups={authStatusBtnGroup}
setter={setFilterAuthStatus}
></FilterButtonGroups>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToSetFilter()}
></button>
</div>
</div>
</motion.div>
</>
)
}

View File

@@ -1,8 +1,7 @@
import moment from 'moment';
import { useEffect } from 'react';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { motion } from 'framer-motion';
import { ChangeEvent, useState } from 'react';
import { useState } from 'react';
import {
AccountHolderSearchType,
AccountHolderSearchFilterProps,
@@ -36,8 +35,8 @@ export const AccountHolderSearchFilter = ({
const [filterMid, setFilterMid] = useState<string>(mid);
const [filterSearchType, setFilterSearchType] = useState<AccountHolderSearchType>(searchType);
const [filterSearchKeyword, setFilterSearchKeyword] = useState<string>(searchKeyword);
const [filterStartDate, setFilterStartDate] = useState<string>(startDate);
const [filterEndDate, setFilterEndDate] = useState<string>(endDate);
const [filterStartDate, setFilterStartDate] = useState<string>(moment(startDate).format('YYYY.MM.DD'));
const [filterEndDate, setFilterEndDate] = useState<string>(moment(endDate).format('YYYY.MM.DD'));
const [filterBank, setFilterBank] = useState<string>(bank)
const [filterProcessResult, setFilterProcessResult] = useState<ProcessResult>(processResult);
const [dateReadOnly, setDateReadyOnly] = useState<boolean>(true);

View File

@@ -1,7 +1,5 @@
import moment from 'moment';
import { SectionTitleArrow } from '@/entities/common/ui/section-title-arrow';
import { AdditionalServiceCategory, DetailInfoSectionProps } from '../../model/types';
import { SlideDown } from 'react-slidedown';
import 'react-slidedown/lib/slidedown.css';
export const DetailInfoWrap = ({
@@ -9,13 +7,12 @@ export const DetailInfoWrap = ({
detailInfo
}: DetailInfoSectionProps) => {
console.log("DetailInfo Check: ", detailInfo)
return (
<>
<div className="txn-section">
<div className="section-title"> </div>
<ul className="kv-list">
{/*링크결제_발송내역*/}
{(additionalServiceCategory === AdditionalServiceCategory.LinkPaymentHistory) &&
<>
< li className="kv-row">
@@ -36,6 +33,73 @@ export const DetailInfoWrap = ({
</li>
</>
}
{/*계좌성명조회*/}
{(additionalServiceCategory === AdditionalServiceCategory.AccountHolderSearch) &&
<>
<li className="kv-row">
<span className="k"> </span>
<span className="v">{detailInfo?.requestDate && moment(detailInfo.requestDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.resultStatus}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.failureReason}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.bankName}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k"> </span>
<span className="v">{detailInfo?.requestWay}</span>
</li>
</>
}
{/*계좌점유인증*/}
{(additionalServiceCategory === AdditionalServiceCategory.AccountHolderAuth) &&
<>
< li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.companyName}</span>
</li>
<li className="kv-row">
<span className="k">MID</span>
<span className="v">{detailInfo?.mid}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.requestDate && detailInfo?.requestDate}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.bankName}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.accountName}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.transferStatus}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{detailInfo?.failureReason && detailInfo?.failureReason}</span>
</li>
</>
}
</ul >
</div >
</>

View File

@@ -1,5 +1,4 @@
import moment from 'moment';
import { motion } from 'framer-motion';
import { NumericFormat } from 'react-number-format';
import { AdditionalServiceCategory, DetailInfoSectionKeys } from '../../model/types';
import { DetailInfoSectionProps } from '../../model/types';
@@ -16,26 +15,32 @@ export const TitleInfoWrap = ({
}
};
let newTitleInfo: Record<string, any> | undefined = titleInfo;
console.log("NewTitleInfo Check: ", newTitleInfo)
const onClickToDownloadConfirmation = () => {
};
return (
<>
{/* 계좌성명조회*/}
{additionalServiceCategory === AdditionalServiceCategory.AccountHolderSearch && (
<>
<div className="num-amount">
<span className="amount">{titleInfo?.accountNo}</span>
</div>
<div className="num-store">{titleInfo?.bankName}</div>
<div className="num-day">{titleInfo?.requestDate}</div>
<div className="num-day">{titleInfo?.requestDate && moment(titleInfo.requestDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</div>
</>
)}
{additionalServiceCategory === AdditionalServiceCategory.AccountHolderAuth && (
<>
<div className="num-amount">
<span className="amount">{titleInfo?.accountName}</span>
</div>
<div className="num-store">{titleInfo?.accountNo}</div>
<div className="num-day">{titleInfo?.scheduledSendDate && moment(titleInfo.scheduledSendDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')} [ ]</div>
</>
)}
{/*링크결제_발송내역*/}
{additionalServiceCategory === AdditionalServiceCategory.LinkPaymentHistory && (
<>
<div className="num-amount">
@@ -52,6 +57,7 @@ export const TitleInfoWrap = ({
<div className="num-day">{titleInfo?.sendDate}</div>
</>
)}
{/*링크결제_발송대기*/}
{additionalServiceCategory === AdditionalServiceCategory.LinkPaymentPending && (
<>
<>

View File

@@ -283,6 +283,13 @@ export const ListItem = ({
{resultStatus === 'SUCCESS' ? '성공' : '실패'}
</div>
);
}
else if (additionalServiceCategory === AdditionalServiceCategory.AccountHolderAuth) {
rs.push(
<div className={`status-label ${(transferStatus === 'REQUEST' || transferStatus === 'SUCCESS') ? 'success' : 'fail'}`}>
{(transferStatus === 'REQUEST' || transferStatus === 'SUCCESS') ? '성공' : '실패'}
</div>
);
}
else if (additionalServiceCategory === AdditionalServiceCategory.KeyInPayment) {
rs.push(
@@ -296,13 +303,6 @@ export const ListItem = ({
</div>
);
}
else if (additionalServiceCategory === AdditionalServiceCategory.AccountHolderAuth) {
rs.push(
<div className={`status-label ${(transferStatus === 'REQUEST' || transferStatus === 'SUCCESS') ? 'success' : 'fail'}`}>
{(transferStatus === 'REQUEST' || transferStatus === 'SUCCESS') ? '성공' : '실패'}
</div>
);
}
else if (additionalServiceCategory === AdditionalServiceCategory.LinkPaymentHistory ||
additionalServiceCategory === AdditionalServiceCategory.LinkPaymentPending
) {

View File

@@ -3,7 +3,7 @@ import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { HeaderType } from '@/entities/common/model/types';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { SortByKeys, AuthAndTransferStatus, AccountHolderAuthListItem } from '@/entities/additional-service/model/types';
import { SortByKeys, AuthAndTransferStatus, AccountHolderAuthListItem } from '@/entities/additional-service/model/types';
import { useEffect, useState } from 'react';
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constant';
import { SortOptionsBox } from '@/entities/additional-service/ui/sort-options-box';
@@ -17,6 +17,8 @@ import {
import { useExtensionAccountHolderAuthListMutation } from '@/entities/additional-service/api/account-holder-auth/use-extension-account-holder-auth-list-mutation';
import { authStatusBtnGroup } from '@/entities/additional-service/model/account-holder-auth/constant';
import { AccountHolderAuthList } from '@/entities/additional-service/ui/account-holder-auth/account-holder-auth-list';
import { useExtensionAccountHolderAuthDownloadExcelMutation } from '@/entities/additional-service/api/account-holder-auth/use-extension-account-holder-auth-download-excel-mutation';
import { AccountHolderAuthFilter } from '@/entities/additional-service/ui/account-holder-auth/filter/account-holder-auth-filter';
export const AccountHolderAuthPage = () => {
const { navigate } = useNavigate();
@@ -38,7 +40,7 @@ export const AccountHolderAuthPage = () => {
});
const { mutateAsync: accountHolderAuthList } = useExtensionAccountHolderAuthListMutation();
const { mutateAsync: downloadExcel } = useExtensionAccountHolderAuthDownloadExcelMutation();
const callList = (option?: {
sortBy?: string,
val?: string
@@ -78,7 +80,14 @@ export const AccountHolderAuthPage = () => {
};
const onClickToDownloadExcel = () => {
downloadExcel({
mid: mid,
fromDate: fromDate,
toDate: toDate,
authStatus: authStatus
}).then((rs) => {
console.log('Excel Downlaod Status : ' + rs.status)
});
}
const onClickToOpenFilter = () => {
@@ -147,7 +156,7 @@ export const AccountHolderAuthPage = () => {
authStatusBtnGroup.map((value, index) => (
<span
key={`key-service-code=${index}`}
className={`keyword-tag ${( authStatus === value.value) ? 'active' : ''}`}
className={`keyword-tag ${(authStatus === value.value) ? 'active' : ''}`}
onClick={() => onClickToAuthStatus(value.value)}
>{value.name}</span>
))
@@ -162,6 +171,19 @@ export const AccountHolderAuthPage = () => {
</div>
</div>
</main>
<AccountHolderAuthFilter
filterOn={filterOn}
setFilterOn={setFilterOn}
mid={mid}
startDate={fromDate}
endDate={toDate}
authStatus={authStatus}
setMid={setMid}
setStartDate={setFromDate}
setEndDate={setToDate}
setAuthStatus={setAuthStatus}
>
</AccountHolderAuthFilter>
</>
);
};

View File

@@ -0,0 +1,72 @@
import { useEffect, useState } from 'react';
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { HeaderType } from '@/entities/common/model/types';
import {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
import { AdditionalServiceCategory, DetailInfo, DetailResponse, ExtensionAccountHolderAuthDetailParams, TitleInfo } from '@/entities/additional-service/model/types';
import { TitleInfoWrap } from '@/entities/additional-service/ui/info-wrap/title-info-wrap';
import { useLocation } from 'react-router';
import { DetailInfoWrap } from '@/entities/additional-service/ui/info-wrap/detail-info-wrap';
import { useExtensionAccountHolderAuthDetailMutation } from '@/entities/additional-service/api/account-holder-auth/use-extension-account-holder-auth-deatil-mutation';
export const AccountHolderAuthDetailPage = () => {
const { navigate } = useNavigate();
const location = useLocation();
const { mid, tid } = location.state || {};
const [titleInfo, setTitleInfo] = useState<TitleInfo>();
const [detailInfo, setDetailInfo] = useState<DetailInfo>();
useSetHeaderTitle('계좌점유인증 상세');
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.additionalService.accountHolderAuth.list);
});
const { mutateAsync: accountHolderAuthDetail } = useExtensionAccountHolderAuthDetailMutation();
const callDetail = () => {
let accountHolderAuthDetailParams: ExtensionAccountHolderAuthDetailParams = {
mid: mid,
tid: tid
}
accountHolderAuthDetail(accountHolderAuthDetailParams).then((rs: DetailResponse) => {
console.log("Detail Info: ", rs)
setTitleInfo(rs.titleInfo);
setDetailInfo(rs.detailInfo);
});
};
useEffect(() => {
callDetail();
}, []);
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane sub active">
<div className="pay-top">
<TitleInfoWrap
additionalServiceCategory={AdditionalServiceCategory.AccountHolderAuth}
titleInfo={titleInfo}
></TitleInfoWrap>
</div>
<div className="pay-detail">
<div className="detail-divider"></div>
<DetailInfoWrap
additionalServiceCategory={AdditionalServiceCategory.AccountHolderAuth}
detailInfo={detailInfo}
></DetailInfoWrap>
</div>
</div>
</div>
</main >
</>
)
}

View File

@@ -33,6 +33,7 @@ import { KeyInPaymentRequestPage } from './key-in-payment/requeset-page';
import { KeyInPaymentRequestSuccessPage } from './key-in-payment/request-success-page';
import { AccountHolderSearchRequestPage } from './account-holder-search/request-page';
import { AccountHolderSearchDetailPage } from './account-holder-search/detail-page';
import { AccountHolderAuthDetailPage } from './account-holder-auth/detail-page';
export const AdditionalServicePages = () => {
return (
@@ -58,7 +59,7 @@ export const AdditionalServicePages = () => {
</Route>
<Route path={ROUTE_NAMES.additionalService.accountHolderAuth.base}>
<Route path={ROUTE_NAMES.additionalService.accountHolderAuth.list} element={<AccountHolderAuthPage/>} />
<Route path={ROUTE_NAMES.additionalService.accountHolderAuth.detail} element={<AccountHolderSearchDetailPage />} />
<Route path={ROUTE_NAMES.additionalService.accountHolderAuth.detail} element={<AccountHolderAuthDetailPage/>} />
</Route>
<Route path={ROUTE_NAMES.additionalService.linkPayment.base}>
<Route path={ROUTE_NAMES.additionalService.linkPayment.shippingHistory} element={<LinkPaymentHistoryPage />} />