Refactor additional service details and fix various bugs

- Convert detail pages to modal components for better UX
- Fix seq type from string to number for ARS and Alimtalk
- Add seq field to list item types
- Fix validation for card number input (remove formatting chars)
- Fix SMS payment resend seq parameter issue
- Improve z-index handling for snackBar and dialogs
- Add useSetHeaderTitle to link payment history wrap
- Remove unused detail page files
- Update payout filter and various detail components

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
HyeonJongKim
2025-11-05 19:41:05 +09:00
parent 50f062b3cf
commit 24435e47d6
30 changed files with 790 additions and 1869 deletions

View File

@@ -1,7 +1,7 @@
VITE_APP_ENV=development
# VITE_APP_AUTH_PROXY_HOST='http://3.35.79.250:8090'
# VITE_APP_API_PROXY_HOST='http://3.35.79.250:8080'
VITE_APP_AUTH_PROXY_HOST='https://auth.nicepay.co.kr'
VITE_APP_API_PROXY_HOST='https://rest.nicepay.co.kr'
VITE_APP_AUTH_PROXY_HOST='http://3.35.79.250:8090'
VITE_APP_API_PROXY_HOST='http://3.35.79.250:8080'
# VITE_APP_AUTH_PROXY_HOST='https://auth.nicepay.co.kr'
#VITE_APP_API_PROXY_HOST='https://rest.nicepay.co.kr'
GENERATE_SOURCEMAP=false
SENTRY_AUTH_TOKEN=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

View File

@@ -46,7 +46,16 @@ export interface ExtensionAlimtalkSettingSaveParams {
sendUserInfo?: SendUserInfo;
};
export interface ExtensionAlimtalkSettingSaveResponse {};
export interface ExtensionAlimtalkSettingSaveResponse {
status: boolean;
error?: {
root?: string;
errKey?: string;
code?: string;
message?: string;
timestamp?: string;
};
};
export interface ExtensionAlimtalkSettingDetailParams {
mid: string;

View File

@@ -332,7 +332,19 @@ export interface ExtensionLinkPayWaitDeleteParams extends ExtensionRequestParams
}
export interface ExtensionLinkPayWaitDeleteRespone {
status: boolean
status: boolean;
error?: {
root?: string;
errKey?: string;
code?: string;
message?: string;
timestamp?: string;
details?: {
validationErrors?: {
[key: string]: string;
}
}
}
}
// 분리승인 상세 조회 Request
@@ -369,7 +381,7 @@ export interface ExtensionLInkPaySeparateActionParams extends ExtensionRequestPa
}
export interface ExtensionLinkPaySeparateActionResponse {
success : boolean;
success: boolean;
totalCount: number;
successCount: number;
failCount: number;

View File

@@ -8,6 +8,7 @@ import { getAuthResultStatusText } from '@/entities/additional-service/model/fac
import { useTranslation } from 'react-i18next';
import { DetailMotionDuration, DetailMotionStyle, DetailMotionVariants } from '@/entities/common/model/constant';
import { FullMenuClose } from '@/entities/common/ui/full-menu-close';
import { showAlert } from '@/widgets/show-alert';
export interface AccountHolderAuthDetailProps {
detailOn: boolean;
@@ -33,7 +34,12 @@ export const AccountHolderAuthDetail = ({
tid: tid
}
accountHolderAuthDetail(accountHolderAuthDetailParams).then((rs: ExtensionAccountHolderAuthDetailResponse) => {
setDetail(rs);
setDetail(rs);
}).catch((e: any) => {
if (e.response?.data?.error?.message) {
showAlert(e.response?.data?.error?.message);
return;
}
});
};
const onClickToClose = () => {
@@ -45,77 +51,77 @@ export const AccountHolderAuthDetail = ({
};
useEffect(() => {
if(!!mid && !!tid){
if (!!mid && !!tid) {
callDetail();
}
}, [mid, tid]);
return (
<>
<motion.div
className="full-menu-modal"
<motion.div
className="full-menu-modal"
initial="hidden"
animate={ (detailOn)? 'visible': 'hidden' }
variants={ DetailMotionVariants }
transition={ DetailMotionDuration }
style={ DetailMotionStyle }
animate={(detailOn) ? 'visible' : 'hidden'}
variants={DetailMotionVariants}
transition={DetailMotionDuration}
style={DetailMotionStyle}
>
<div className="full-menu-container pdw-16">
<div className="full-menu-header">
<div className="full-menu-title center">{ t('additionalService.accountHolderAuth.detailTitle') }</div>
<div className="full-menu-title center">{t('additionalService.accountHolderAuth.detailTitle')}</div>
<div className="full-menu-actions">
<FullMenuClose
addClass="full-menu-close"
onClickToCallback={ onClickToClose }
onClickToCallback={onClickToClose}
></FullMenuClose>
</div>
</div>
<div className="pay-top">
<div className="num-amount">
<span className="amount">{detail?.accountName}</span>
</div>
<span className="num-store">{detail?.accountNo}</span>
<div className="num-day">{getDate(detail?.requestDate)}</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('transaction.sections.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('transaction.fields.companyName')}</span>
<span className="v">{detail?.companyName}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.mid')}</span>
<span className="v">{detail?.mid}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.accountHolderAuth.requestDate')}</span>
<span className="v">{getDate(detail?.requestDate)}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.bank')}</span>
<span className="v">{detail?.bankName}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.accountNo')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.accountHolder')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('common.result')}</span>
<span className="v">{getAuthStatusText(t)(detail?.authStatus)}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.failureReason')}</span>
<span className="v">{detail?.failReason ? getAuthResultStatusText(t)(detail?.failReason) : '-' }</span>
</li>
</ul>
<div className="pay-top">
<div className="num-amount">
<span className="amount">{detail?.accountName}</span>
</div>
<span className="num-store">{detail?.accountNo}</span>
<div className="num-day">{getDate(detail?.requestDate)}</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('transaction.sections.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('transaction.fields.companyName')}</span>
<span className="v">{detail?.companyName}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.mid')}</span>
<span className="v">{detail?.mid}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.accountHolderAuth.requestDate')}</span>
<span className="v">{getDate(detail?.requestDate)}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.bank')}</span>
<span className="v">{detail?.bankName}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.accountNo')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.accountHolder')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('common.result')}</span>
<span className="v">{getAuthStatusText(t)(detail?.authStatus)}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.failureReason')}</span>
<span className="v">{detail?.failReason ? getAuthResultStatusText(t)(detail?.failReason) : '-'}</span>
</li>
</ul>
</div>
</div>
</motion.div>
</>
);

View File

@@ -10,6 +10,7 @@ import { useExtensionArsDetailMutation } from '@/entities/additional-service/api
import { useExtensionArsResendMutation } from '@/entities/additional-service/api/ars/use-extension-ars-resend-mutation';
import { snackBar } from '@/shared/lib';
import { ArsResendSmsBottomSheet } from '../resend-sms-bottom-sheet';
import { showAlert } from '@/widgets/show-alert';
export interface ArsDetailProps {
detailOn: boolean;
@@ -39,6 +40,11 @@ export const ArsDetail = ({
};
extensionArsDetail(arsDetailParams).then((rs: ExtensionArsDetailResponse) => {
setDetail(rs);
}).catch((e: any) => {
if (e.response?.data?.error?.message) {
showAlert(e.response?.data?.error?.message);
return;
}
});
}
@@ -101,76 +107,76 @@ export const ArsDetail = ({
></FullMenuClose>
</div>
</div>
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(Number(detail?.amount) || 0) })}
</span>
</div>
<div className="num-store">{detail?.corpName}</div>
<div className="num-day">{getDate(detail?.paymentDate)}</div>
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(Number(detail?.amount) || 0) })}
</span>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.ars.transactionInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('transaction.fields.mid')}</span>
<span className="v">{detail?.mid}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.paymentMethod')}</span>
<span className="v">{detail?.arsPaymentMethod}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.paymentStatus')}</span>
<span className="v">{getArsPaymentStatusName(t)(detail?.paymentStatus)}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.orderStatus')}</span>
<span className="v">{getArsOrderStatusName(t)(detail?.orderStatus)}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.orderDateTime')}</span>
<span className="v">{
detail?.paymentDate ? moment(detail.paymentDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'
}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.productName')}</span>
<span className="v">{detail?.goodsName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.orderNumber')}</span>
<span className="v">{detail?.tid}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.buyer')}</span>
<span className="v">{detail?.buyerName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.phoneNumber')}</span>
<span className="v">{detail?.maskPhoneNumber}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.email')}</span>
<span className="v">{detail?.email}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.sendVerificationCode')}</span>
<span className="v">{detail?.smsVerificationCode}</span>
</li>
</ul>
<div className="num-store">{detail?.corpName}</div>
<div className="num-day">{getDate(detail?.paymentDate)}</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.ars.transactionInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('transaction.fields.mid')}</span>
<span className="v">{detail?.mid}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.paymentMethod')}</span>
<span className="v">{detail?.arsPaymentMethod}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.paymentStatus')}</span>
<span className="v">{getArsPaymentStatusName(t)(detail?.paymentStatus)}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.orderStatus')}</span>
<span className="v">{getArsOrderStatusName(t)(detail?.orderStatus)}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.orderDateTime')}</span>
<span className="v">{
detail?.paymentDate ? moment(detail.paymentDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'
}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.productName')}</span>
<span className="v">{detail?.goodsName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.orderNumber')}</span>
<span className="v">{detail?.tid}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.buyer')}</span>
<span className="v">{detail?.buyerName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.phoneNumber')}</span>
<span className="v">{detail?.maskPhoneNumber}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.email')}</span>
<span className="v">{detail?.email}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.ars.sendVerificationCode')}</span>
<span className="v">{detail?.smsVerificationCode}</span>
</li>
</ul>
</div>
{detail?.arsPaymentMethod === ArsPaymentMethod.SMS && (
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToOpenResendBottomSheet()}
//disabled={ detail?.orderStatus !== OrderStatus.PENDING }
>{t('additionalService.ars.smsResend')}</button>
</div>
{detail?.arsPaymentMethod === ArsPaymentMethod.SMS && (
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToOpenResendBottomSheet()}
//disabled={ detail?.orderStatus !== OrderStatus.PENDING }
>{t('additionalService.ars.smsResend')}</button>
</div>
)}
)}
</div>
<ArsResendSmsBottomSheet
setBottomSheetOn={setBottomSheetOn}

View File

@@ -13,6 +13,7 @@ import { useExtensionFundAccountDownloadReceiptMutation } from '@/entities/addit
import { EmailBottomSheet } from '@/entities/common/ui/email-bottom-sheet';
import { DetailMotionDuration, DetailMotionStyle, DetailMotionVariants } from '@/entities/common/model/constant';
import { FullMenuClose } from '@/entities/common/ui/full-menu-close';
import { showAlert } from '@/widgets/show-alert';
export interface FundAccountResultDetailProps {
detailOn: boolean;
@@ -26,8 +27,8 @@ export const FundAccountResultDetail = ({
mid,
tid
}: FundAccountResultDetailProps) => {
const { t } = useTranslation();
const { t } = useTranslation();
const [detail, setDetail] = useState<ExtensionFundAccountResultDetailResponse>();
const [emailBottomSheetOn, setEmailBottomSheetOn] = useState<boolean>(false);
@@ -43,6 +44,11 @@ export const FundAccountResultDetail = ({
extensionFundAccountResultDetail(params).then((rs: ExtensionFundAccountResultDetailResponse) => {
console.log(rs.requestDate)
setDetail(rs);
}).catch((e: any) => {
if (e.response?.data?.error?.message) {
showAlert(e.response?.data?.error?.message);
return;
}
});
};
@@ -51,7 +57,7 @@ export const FundAccountResultDetail = ({
};
const onSendRequest = (selectedEmail?: string) => {
if(selectedEmail){
if (selectedEmail) {
let params: ExtensionFundAccountDownloadReceiptParams = {
mid: mid,
tid: tid,
@@ -63,107 +69,107 @@ export const FundAccountResultDetail = ({
}
setEmailBottomSheetOn(false);
};
const onClickToClose = () => {
setDetailOn(false);
};
useEffect(() => {
if(!!mid && !!tid){
if (!!mid && !!tid) {
callDetail();
}
}, [mid, tid]);
return (
<>
<motion.div
className="full-menu-modal"
<motion.div
className="full-menu-modal"
initial="hidden"
animate={ (detailOn)? 'visible': 'hidden' }
variants={ DetailMotionVariants }
transition={ DetailMotionDuration }
style={ DetailMotionStyle }
animate={(detailOn) ? 'visible' : 'hidden'}
variants={DetailMotionVariants}
transition={DetailMotionDuration}
style={DetailMotionStyle}
>
<div className="full-menu-container pdw-16">
<div className="full-menu-header">
<div className="full-menu-title center">{ t('additionalService.fundAccount.transferDetailTitle') }</div>
<div className="full-menu-title center">{t('additionalService.fundAccount.transferDetailTitle')}</div>
<div className="full-menu-actions">
<FullMenuClose
addClass="full-menu-close"
onClickToCallback={ onClickToClose }
onClickToCallback={onClickToClose}
></FullMenuClose>
</div>
</div>
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(detail?.amount || 0) })}
</span>
</div>
<div className="num-store">{detail?.accountName}({detail?.accountNo})</div>
{detail?.applicationDate && (
<div className="num-day"> {moment(detail?.applicationDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</div>
)}
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(detail?.amount || 0) })}
</span>
</div>
{/* ✅ resultMessage가 "정상"일 때만 표시 */}
{detail?.resultMessage === '정상' && (
<div className="receipt-row">
<button
type="button"
className="receipt-btn"
onClick={ onClickToOpenEmailBottomSheet }
>
<span className="icon-24 download"></span>
<span>{t('additionalService.fundAccount.depositCertificate')}</span>
</button>
</div>
<div className="num-store">{detail?.accountName}({detail?.accountNo})</div>
{detail?.applicationDate && (
<div className="num-day"> {moment(detail?.applicationDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</div>
)}
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.fundAccount.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.requestDateTime')}</span>
<span className="v">{moment(detail?.requestDate,'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.transferDateTime')}</span>
<span className="v">{detail?.applicationDate ? moment(detail?.applicationDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.transferResult')}</span>
<span className="v">{detail?.resultMessage || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.failureReason')}</span>
<span className="v">{detail?.failReason || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.beneficiaryName')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.bank')}</span>
<span className="v">{detail?.bankName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.accountNumber')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">MID</span>
<span className="v">{mid}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.orderNumber')}</span>
<span className="v">{detail?.moid}</span>
</li>
<li className="kv-row">
<span className="k">TID</span>
<span className="v">{detail?.tid}</span>
</li>
</ul>
</div>
{/* ✅ resultMessage가 "정상"일 때만 표시 */}
{detail?.resultMessage === '정상' && (
<div className="receipt-row">
<button
type="button"
className="receipt-btn"
onClick={onClickToOpenEmailBottomSheet}
>
<span className="icon-24 download"></span>
<span>{t('additionalService.fundAccount.depositCertificate')}</span>
</button>
</div>
)}
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.fundAccount.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.requestDateTime')}</span>
<span className="v">{moment(detail?.requestDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.transferDateTime')}</span>
<span className="v">{detail?.applicationDate ? moment(detail?.applicationDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.transferResult')}</span>
<span className="v">{detail?.resultMessage || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.failureReason')}</span>
<span className="v">{detail?.failReason || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.beneficiaryName')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.bank')}</span>
<span className="v">{detail?.bankName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.accountNumber')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">MID</span>
<span className="v">{mid}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.orderNumber')}</span>
<span className="v">{detail?.moid}</span>
</li>
<li className="kv-row">
<span className="k">TID</span>
<span className="v">{detail?.tid}</span>
</li>
</ul>
</div>
</div>
</motion.div>
<EmailBottomSheet

View File

@@ -11,6 +11,7 @@ import { snackBar } from '@/shared/lib';
import { useExtensionFundAccountTransferRequestMutation } from '@/entities/additional-service/api/fund-account/use-extension-fund-account-transfer-request-mutation';
import { DetailMotionDuration, DetailMotionStyle, DetailMotionVariants } from '@/entities/common/model/constant';
import { FullMenuClose } from '@/entities/common/ui/full-menu-close';
import { showAlert } from '@/widgets/show-alert';
export interface FundAccountTransferDetailProps {
detailOn: boolean;
@@ -39,27 +40,35 @@ export const FundAccountTransferDetail = ({
extensionFundAccountTransferDetail(params).then((rs: ExtensionFundAccountTransferDetailResponse) => {
setDetail(rs);
});
}).catch((e: any) => {
if (e.response?.data?.error?.message) {
showAlert(e.response?.data?.error?.message);
return;
}
});;
};
const onClickToRequest = () => {
let params: ExtensionFundAccountTransferRequestParams = {
seq: seq
};
extensionFundAccountTransferRequest(params).then((rs: ExtensionFundAccountTransferRequestResponse) => {
if(rs.status){
if (rs.status) {
callDetail();
snackBar(t('additionalService.fundAccount.transferRequestSuccess'))
}
else{
else {
const errorMessage = rs.error?.message || t('additionalService.fundAccount.transferRequestFailed');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
}
}).catch((error) => {
const errorMessage = error?.response?.data?.error?.message ||
error?.message ||
t('additionalService.fundAccount.transferRequestError');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
console.log(error);
const errorMessage = error?.response?.data?.error?.message || error?.message || t('additionalService.fundAccount.transferRequestError');
if (error.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${error.response?.data?.error?.message}`)
} else {
showAlert(`[${t('common.failed')}] ${errorMessage}`);
}
});
};
@@ -68,92 +77,92 @@ export const FundAccountTransferDetail = ({
};
useEffect(() => {
if(!!seq){
if (!!seq) {
callDetail();
}
}, [seq]);
return (
<>
<motion.div
className="full-menu-modal"
<motion.div
className="full-menu-modal"
initial="hidden"
animate={ (detailOn)? 'visible': 'hidden' }
variants={ DetailMotionVariants }
transition={ DetailMotionDuration }
style={ DetailMotionStyle }
animate={(detailOn) ? 'visible' : 'hidden'}
variants={DetailMotionVariants}
transition={DetailMotionDuration}
style={DetailMotionStyle}
>
<div className="full-menu-container pdw-16">
<div className="full-menu-header">
<div className="full-menu-title center">{ t('additionalService.fundAccount.transferDetailTitle') }</div>
<div className="full-menu-title center">{t('additionalService.fundAccount.transferDetailTitle')}</div>
<div className="full-menu-actions">
<FullMenuClose
addClass="full-menu-close"
onClickToCallback={ onClickToClose }
onClickToCallback={onClickToClose}
></FullMenuClose>
</div>
</div>
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(detail?.amount || 0) })}
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(detail?.amount || 0) })}
</span>
</div>
<div className="num-store">{detail?.accountName}({detail?.accountNo})</div>
<div className="num-day">
{detail?.registDate ? moment(detail.registDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'}
</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.fundAccount.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.registrationDateTime')}</span>
<span className="v">
{detail?.registDate ? moment(detail.registDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'}
</span>
</div>
<div className="num-store">{detail?.accountName}({detail?.accountNo})</div>
<div className="num-day">
{detail?.registDate ? moment(detail.registDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'}
</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.fundAccount.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.registrationDateTime')}</span>
<span className="v">
{detail?.registDate ? moment(detail.registDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'}
</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.processingResult')}</span>
<span className="v">{getFundAccountStatusName(t)(detail?.resultStatus) || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.requestDate')}</span>
<span className="v">
{detail?.requestDate ? moment(detail.requestDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD') : '-'}
</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.transferResult')}</span>
<span className="v">{detail?.resultMessage || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.beneficiaryName')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.bank')}</span>
<span className="v">{detail?.bankCode || '-'} </span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.accountNumber')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">MID</span>
<span className="v">{detail?.mid}</span>
</li>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.processingResult')}</span>
<span className="v">{getFundAccountStatusName(t)(detail?.resultStatus) || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.requestDate')}</span>
<span className="v">
{detail?.requestDate ? moment(detail.requestDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD') : '-'}
</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.transferResult')}</span>
<span className="v">{detail?.resultMessage || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.beneficiaryName')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.bank')}</span>
<span className="v">{detail?.bankCode || '-'} </span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.accountNumber')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">MID</span>
<span className="v">{detail?.mid}</span>
</li>
</ul>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToRequest()}
disabled={detail?.resultStatus !== FundAccountStatus.REGIST_COMPLETE}
>{t('additionalService.fundAccount.transferRequestButton')}</button>
</div>
</ul>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToRequest()}
disabled={detail?.resultStatus !== FundAccountStatus.REGIST_COMPLETE}
>{t('additionalService.fundAccount.transferRequestButton')}</button>
</div>
</div>
</motion.div>
</>

View File

@@ -24,6 +24,7 @@ import moment from 'moment';
import { useTranslation } from 'react-i18next';
import { DetailMotionDuration, DetailMotionStyle, DetailMotionVariants } from '@/entities/common/model/constant';
import { FullMenuClose } from '@/entities/common/ui/full-menu-close';
import { showAlert } from '@/widgets/show-alert';
export interface LinkPaymentHistoryDetailProps {
detailOn: boolean;
@@ -66,7 +67,12 @@ export const LinkPaymentHistoryDetail = ({
setDetailInfo(rs.detailInfo)
setPaymentInfo(rs.paymentInfo)
setDetailExposure(rs.detailExposure ?? false)
})
}).catch((e: any) => {
if (e.response?.data?.error?.message) {
showAlert(e.response?.data?.error?.message);
return;
}
});
}
// Resend API
@@ -88,9 +94,15 @@ export const LinkPaymentHistoryDetail = ({
})
.catch((error) => {
const errorMessage = error?.response?.data?.error?.message ||
error?.message ||
t('additionalService.linkPayment.resendError');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
error?.message ||
t('additionalService.linkPayment.resendError');
console.log(error)
if (error.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${errorMessage}`);
}
else {
showAlert(`[${t('common.failed')}] ${errorMessage}`)
}
});
}
@@ -143,64 +155,64 @@ export const LinkPaymentHistoryDetail = ({
};
useEffect(() => {
if(!!mid && !!requestId){
if (!!mid && !!requestId) {
callDetail();
}
}, [mid, requestId, subReqId]);
return (
<>
<motion.div
className="full-menu-modal"
<motion.div
className="full-menu-modal"
initial="hidden"
animate={ (detailOn)? 'visible': 'hidden' }
variants={ DetailMotionVariants }
transition={ DetailMotionDuration }
style={ DetailMotionStyle }
animate={(detailOn) ? 'visible' : 'hidden'}
variants={DetailMotionVariants}
transition={DetailMotionDuration}
style={DetailMotionStyle}
>
<div className="full-menu-container pdw-16">
<div className="full-menu-header">
<div className="full-menu-title center">{ t('additionalService.linkPayment.detailTitle') }</div>
<div className="full-menu-title center">{t('additionalService.linkPayment.detailTitle')}</div>
<div className="full-menu-actions">
<FullMenuClose
addClass="full-menu-close"
onClickToCallback={ onClickToClose }
onClickToCallback={onClickToClose}
></FullMenuClose>
</div>
</div>
<div className="pay-top">
<TitleInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentHistory}
titleInfo={titleInfo}
></TitleInfoWrap>
</div>
<div className="pay-detail">
<div className="detail-divider"></div>
<PaymentInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentHistory}
paymentInfo={paymentInfo}
></PaymentInfoWrap>
<div className="detail-divider"></div>
<DetailInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentHistory}
detailInfo={detailInfo}
></DetailInfoWrap>
<div className="detail-divider"></div>
<div className="link-payment-detail-button" style={{ paddingBottom: '100px' }}>
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToSeparateApproval()}
disabled={detailExposure}
>{t('additionalService.linkPayment.separateApprovalDetail')}</button>
</div>
</div>
<div className="apply-row">
<div className="pay-top">
<TitleInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentHistory}
titleInfo={titleInfo}
></TitleInfoWrap>
</div>
<div className="pay-detail">
<div className="detail-divider"></div>
<PaymentInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentHistory}
paymentInfo={paymentInfo}
></PaymentInfoWrap>
<div className="detail-divider"></div>
<DetailInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentHistory}
detailInfo={detailInfo}
></DetailInfoWrap>
<div className="detail-divider"></div>
<div className="link-payment-detail-button" style={{ paddingBottom: '100px' }}>
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToResend()}
disabled={!isResendEnabled()}
>{t('additionalService.linkPayment.resend')}</button>
onClick={() => onClickToSeparateApproval()}
disabled={detailExposure}
>{t('additionalService.linkPayment.separateApprovalDetail')}</button>
</div>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToResend()}
disabled={!isResendEnabled()}
>{t('additionalService.linkPayment.resend')}</button>
</div>
</div>
</motion.div>
</>
)

View File

@@ -12,6 +12,8 @@ import { snackBar } from '@/shared/lib';
import { useTranslation } from 'react-i18next';
import { DetailMotionDuration, DetailMotionStyle, DetailMotionVariants } from '@/entities/common/model/constant';
import { FullMenuClose } from '@/entities/common/ui/full-menu-close';
import { showAlert } from '@/widgets/show-alert';
import { P } from 'node_modules/framer-motion/dist/types.d-Cjd591yU';
export interface LinkPaymentWaitDetailProps {
detailOn: boolean;
@@ -38,12 +40,15 @@ export const LinkPaymentWaitDetail = ({
mid: mid,
requestId: requestId
}
linkPayWaitDetail(detailParam).then((rs: DetailResponse) => {
setTitleInfo(rs.titleInfo)
setPaymentInfo(rs.paymentInfo)
})
}).catch((e: any) => {
if (e.response?.data?.error?.message) {
showAlert(e.response?.data?.error?.message);
return;
}
});
}
const deletePayment = () => {
@@ -53,11 +58,21 @@ export const LinkPaymentWaitDetail = ({
}
linkPayWaitDelete(deleteParam)
.then((rs) => {
callDetail();
snackBar(t('additionalService.linkPayment.deleteSuccess'))
if (rs.status) {
callDetail();
snackBar(t('additionalService.linkPayment.deleteSuccess'))
} else {
const errorMessage = rs.error?.message || t('additionalService.linkPayment.deleteSuccess');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
}
})
.catch((error) => {
snackBar(`[${t('common.failed')}] ${error?.response?.data?.message}`)
console.log(error)
if (error.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${error?.response?.data?.error?.message}`)
} else {
showAlert(`[${t('common.failed')}] ${error?.response?.data?.error?.message}`)
}
});
}
@@ -89,52 +104,52 @@ export const LinkPaymentWaitDetail = ({
};
useEffect(() => {
if(!!mid && !!requestId){
if (!!mid && !!requestId) {
callDetail();
}
}, [mid, requestId]);
return (
<>
<motion.div
className="full-menu-modal"
<motion.div
className="full-menu-modal"
initial="hidden"
animate={ (detailOn)? 'visible': 'hidden' }
variants={ DetailMotionVariants }
transition={ DetailMotionDuration }
style={ DetailMotionStyle }
animate={(detailOn) ? 'visible' : 'hidden'}
variants={DetailMotionVariants}
transition={DetailMotionDuration}
style={DetailMotionStyle}
>
<div className="full-menu-container pdw-16">
<div className="full-menu-header">
<div className="full-menu-title center">{ t('additionalService.linkPayment.waitDetailTitle') }</div>
<div className="full-menu-title center">{t('additionalService.linkPayment.waitDetailTitle')}</div>
<div className="full-menu-actions">
<FullMenuClose
addClass="full-menu-close"
onClickToCallback={ onClickToClose }
onClickToCallback={onClickToClose}
></FullMenuClose>
</div>
</div>
<div className="pay-top">
<TitleInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentWait}
titleInfo={titleInfo}
></TitleInfoWrap>
</div>
<div className="pay-detail">
<div className="detail-divider"></div>
<PaymentInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentWait}
paymentInfo={paymentInfo}
></PaymentInfoWrap>
</div>
<div className="pay-top">
<TitleInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentWait}
titleInfo={titleInfo}
></TitleInfoWrap>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToCancel()}
disabled={paymentInfo?.processStatus !== LinkPaymentProcessStatus.SEND_REQUEST}
>{t('additionalService.linkPayment.delete')}</button>
<div className="pay-detail">
<div className="detail-divider"></div>
<PaymentInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentWait}
paymentInfo={paymentInfo}
></PaymentInfoWrap>
</div>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToCancel()}
disabled={paymentInfo?.processStatus !== LinkPaymentProcessStatus.SEND_REQUEST}
>{t('additionalService.linkPayment.delete')}</button>
</div>
</motion.div>
</>
)

View File

@@ -9,6 +9,7 @@ import { DetailMotionDuration, DetailMotionStyle, DetailMotionVariants } from '@
import { FullMenuClose } from '@/entities/common/ui/full-menu-close';
import { DownloadTypeBottomSheet } from '@/entities/common/ui/download-type-bottom-sheet';
import { EmailBottomSheet } from '@/entities/common/ui/email-bottom-sheet';
import { showAlert } from '@/widgets/show-alert';
export interface PayoutDetailProps {
@@ -39,9 +40,13 @@ export const PayoutDetail = ({
tid: tid,
mid: mid,
};
extensionPayoutDetail(params).then((rs: ExtensionPayoutDetailResponse) => {
setDetail(rs);
}).catch((e: any) => {
if (e.response?.data?.error?.message) {
showAlert(e.response?.data?.error?.message);
return;
}
});
}
@@ -121,71 +126,71 @@ export const PayoutDetail = ({
></FullMenuClose>
</div>
</div>
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(detail?.disbursementAmount || 0) })}
</span>
</div>
<div className="num-store">{detail?.companyName}</div>
<div className="num-day">{detail?.settlementDate}</div>
<div className="receipt-row">
<button
className="receipt-btn"
type="button"
onClick={onClickToDownload}
>
<span className="icon-24 download"></span>
<span>{t('additionalService.payout.depositCertificate')}</span>
</button>
</div>
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(detail?.disbursementAmount || 0) })}
</span>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.payout.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('additionalService.payout.disbursementStatus')}</span>
<span className="v">{detail?.disbursementStatus}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.transactionType')}</span>
<span className="v">{detail?.transTypeName}</span>
</li>
<li className="kv-row">
<span className="k">{t('common.requestDate')}</span>
<span className="v">{moment(detail?.requestDate).format('YYYY.MM.DD')}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.disbursementDateTime')}</span>
<span className="v">{moment(detail?.settlementDateTime, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.businessNumber')}</span>
<span className="v">{detail?.companyNo}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.accountHolder')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.bank')}</span>
<span className="v">{detail?.bankName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.accountNumber')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.depositor')}</span>
<span className="v">{detail?.depositName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.failureReason')}</span>
<span className="v">{detail?.failReason}</span>
</li>
</ul>
<div className="num-store">{detail?.companyName}</div>
<div className="num-day">{detail?.settlementDate}</div>
<div className="receipt-row">
<button
className="receipt-btn"
type="button"
onClick={onClickToDownload}
>
<span className="icon-24 download"></span>
<span>{t('additionalService.payout.depositCertificate')}</span>
</button>
</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.payout.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('additionalService.payout.disbursementStatus')}</span>
<span className="v">{detail?.disbursementStatus}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.transactionType')}</span>
<span className="v">{detail?.transTypeName}</span>
</li>
<li className="kv-row">
<span className="k">{t('common.requestDate')}</span>
<span className="v">{moment(detail?.requestDate).format('YYYY.MM.DD')}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.disbursementDateTime')}</span>
<span className="v">{moment(detail?.settlementDateTime, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.businessNumber')}</span>
<span className="v">{detail?.companyNo}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.accountHolder')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.bank')}</span>
<span className="v">{detail?.bankName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.accountNumber')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.depositor')}</span>
<span className="v">{detail?.depositName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.failureReason')}</span>
<span className="v">{detail?.failReason}</span>
</li>
</ul>
</div>
</div>
<DownloadTypeBottomSheet
bottomSheetOn={downloadTypeBottomSheetOn}

View File

@@ -65,8 +65,8 @@ export const PayoutFilter = ({
const [filterMid, setFilterMid] = useState<string>(mid);
const [filterSearchDateType, setFilterSearchDateType] = useState<PayoutSearchDateType>(searchDateType);
const [filterFromDate, setFilterFromDate] = useState<string>(moment(fromDate).format('YYYY.MM.DD'));
const [filterToDate, setFilterToDate] = useState<string>(moment(toDate).format('YYYY.MM.DD'));
const [filterFromDate, setFilterFromDate] = useState<string>(moment(fromDate).format('YYYYMMDD'));
const [filterToDate, setFilterToDate] = useState<string>(moment(toDate).format('YYYYMMDD'));
const [filterStatus, setFilterStatus] = useState<PayoutDisbursementStatus>(status);
const [filterMinAmount, setFilterMinAmount] = useState<number | undefined>(minAmount);
const [filterMaxAmount, setFilterMaxAmount] = useState<number | undefined>(maxAmount);

View File

@@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next';
import { IMAGE_ROOT } from '@/shared/constants/common';
import { SmsPaymentDetailResendProps } from '../../../additional-service/model/sms-payment/types';
import { useExtensionSmsResendMutation } from '../../api/sms-payment/use-extension-sms-resend-mutation';
import appBridge from '@/shared/lib/appBridge';
import { snackBar } from '@/shared/lib';
import { showAlert } from '@/widgets/show-alert';
export const SmsPaymentDetailResend = ({
bottomSmsPaymentDetailResendOn,
@@ -19,7 +19,7 @@ export const SmsPaymentDetailResend = ({
visible: { y: '0%' },
};
const {mutateAsync : resendMessage } = useExtensionSmsResendMutation();
const { mutateAsync: resendMessage } = useExtensionSmsResendMutation();
const onClickResend = () => {
// sendMessage가 없으면 재발송 불가
@@ -37,9 +37,13 @@ export const SmsPaymentDetailResend = ({
} else {
snackBar(t('additionalService.sms.sendFailed', { message: rs.error?.message }))
}
}).catch((error) => {
snackBar(t('additionalService.sms.sendFailed', { message: error?.response?.data?.message || error?.response?.data?.error?.message }) || t('additionalService.sms.sendFailedGeneric'))
setBottomSmsPaymentDetailResendOn(false)
}).catch((e: any) => {
console.log(e)
if (e.response?.data?.error?.message) {
showAlert(e.response?.data?.error?.message);
setBottomSmsPaymentDetailResendOn(false)
return;
}
});
}
@@ -50,14 +54,14 @@ export const SmsPaymentDetailResend = ({
return (
<>
{ bottomSmsPaymentDetailResendOn &&
{bottomSmsPaymentDetailResendOn &&
<div className="bg-dim"></div>
}
<motion.div
<motion.div
className="bottomsheet"
initial="hidden"
animate={ (bottomSmsPaymentDetailResendOn)? 'visible': 'hidden' }
variants={ variants }
animate={(bottomSmsPaymentDetailResendOn) ? 'visible' : 'hidden'}
variants={variants}
transition={{ duration: 0.5 }}
>
<div className="bottomsheet-header">
@@ -68,9 +72,9 @@ export const SmsPaymentDetailResend = ({
type="button"
>
<img
src={ IMAGE_ROOT + '/ico_close.svg' }
src={IMAGE_ROOT + '/ico_close.svg'}
alt={t('common.close')}
onClick={ () => onClickToClose() }
onClick={() => onClickToClose()}
/>
</button>
</div>

View File

@@ -26,6 +26,8 @@ import { EmailBottomSheet } from '@/entities/common/ui/email-bottom-sheet';
import { useExtensionAccessCheck } from '@/shared/lib/hooks/use-extension-access-check';
import useIntersectionObserver from '@/widgets/intersection-observer';
import { AccountHolderAuthDetail } from '@/entities/additional-service/ui/account-holder-auth/detail/account-holder-auth-detail';
import { showAlert } from '@/widgets/show-alert';
import { checkGrant } from '@/shared/lib/check-grant';
export const AccountHolderAuthPage = () => {
const { navigate } = useNavigate();
@@ -63,18 +65,18 @@ export const AccountHolderAuthPage = () => {
const { mutateAsync: downloadExcel } = useExtensionAccountHolderAuthDownloadExcelMutation();
const onIntersect: IntersectionObserverCallback = (entries: Array<IntersectionObserverEntry>) => {
entries.forEach((entry: IntersectionObserverEntry) => {
if(entry.isIntersecting){
if(onActionIntersect && !!pageParam.cursor){
if (entry.isIntersecting) {
if (onActionIntersect && !!pageParam.cursor) {
setOnActionIntersect(false);
callList('page');
}
}
}
});
};
const { setTarget } = useIntersectionObserver({
threshold: 1,
onIntersect
const { setTarget } = useIntersectionObserver({
threshold: 1,
onIntersect
});
const callList = (type?: string) => {
@@ -88,37 +90,37 @@ export const AccountHolderAuthPage = () => {
...{ sortType: sortType }
}
};
if(type !== 'page' && listParams.page){
if (type !== 'page' && listParams.page) {
listParams.page.cursor = null;
}
accountHolderAuthList(listParams).then((rs) => {
if(type === 'page'){
if (type === 'page') {
setListItems([
...listItems,
...rs.content
]);
}
else{
else {
setListItems(rs.content);
}
if(rs.hasNext
if (rs.hasNext
&& rs.nextCursor !== pageParam.cursor
&& rs.content.length === DEFAULT_PAGE_PARAM.size
){
) {
setPageParam({
...pageParam,
...{ cursor: rs.nextCursor }
});
}
else{
else {
setPageParam({
...pageParam,
...{ cursor: null }
});
}
setOnActionIntersect(
!!rs.hasNext
!!rs.hasNext
&& rs.nextCursor !== pageParam.cursor
&& rs.content.length === DEFAULT_PAGE_PARAM.size
);
@@ -139,10 +141,10 @@ export const AccountHolderAuthPage = () => {
};
const setDetailData = (detailData: DetailData) => {
setDetailOn(detailData.detailOn);
if(detailData.mid){
if (detailData.mid) {
setDetailMid(detailData.mid);
}
if(detailData.tid){
if (detailData.tid) {
setDetailTid(detailData.tid);
}
};
@@ -154,7 +156,7 @@ export const AccountHolderAuthPage = () => {
const onSendRequest = (selectedEmail?: string) => {
if (selectedEmail) {
const params: ExtensionAccountHolderAuthDownloadExcelParams = { // 추후 수정필요
const params: ExtensionAccountHolderAuthDownloadExcelParams = {
mid: mid,
email: selectedEmail,
fromDate: fromDate,
@@ -244,12 +246,12 @@ export const AccountHolderAuthPage = () => {
</div>
</div>
<AccountHolderAuthList
additionalServiceCategory={ AdditionalServiceCategory.AccountHolderAuth }
listItems={ listItems }
mid={ mid }
setDetailData={ setDetailData }
additionalServiceCategory={AdditionalServiceCategory.AccountHolderAuth}
listItems={listItems}
mid={mid}
setDetailData={setDetailData}
></AccountHolderAuthList>
<div ref={ setTarget }></div>
<div ref={setTarget}></div>
</div>
</div>
</main>
@@ -266,10 +268,10 @@ export const AccountHolderAuthPage = () => {
setAuthStatus={setAuthStatus}
></AccountHolderAuthFilter>
<AccountHolderAuthDetail
detailOn={ detailOn }
setDetailOn={ setDetailOn }
mid={ detailMid }
tid={ detailTid }
detailOn={detailOn}
setDetailOn={setDetailOn}
mid={detailMid}
tid={detailTid}
></AccountHolderAuthDetail>
<EmailBottomSheet
bottomSheetOn={emailBottomSheetOn}

View File

@@ -1,109 +0,0 @@
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, 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';
import { ExtensionAccountHolderAuthDetailParams, ExtensionAccountHolderAuthDetailResponse } from '@/entities/additional-service/model/account-holder-auth/types';
import moment from 'moment';
import { getAuthStatusText } from '@/entities/additional-service/model/account-holder-auth/constant';
import { getAuthResultStatusText } from '@/entities/additional-service/model/face-auth/constant';
import { useTranslation } from 'react-i18next';
export const AccountHolderAuthDetailPage = () => {
const { navigate } = useNavigate();
const { t } = useTranslation();
const location = useLocation();
const { mid, tid } = location.state || {};
const [detail, setDetail] = useState<ExtensionAccountHolderAuthDetailResponse>();
useSetHeaderTitle(t('additionalService.accountHolderAuth.detailTitle'));
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: ExtensionAccountHolderAuthDetailResponse) => {
setDetail(rs);
});
};
useEffect(() => {
callDetail();
}, []);
const getDate = (date?: string) => {
return (date) ? moment(date, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '';
};
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane sub active">
<div className="pay-top">
<div className="num-amount">
<span className="amount">{detail?.accountName}</span>
</div>
<span className="num-store">{detail?.accountNo}</span>
<div className="num-day">{getDate(detail?.requestDate)}</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('transaction.sections.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('transaction.fields.companyName')}</span>
<span className="v">{detail?.companyName}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.mid')}</span>
<span className="v">{detail?.mid}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.accountHolderAuth.requestDate')}</span>
<span className="v">{getDate(detail?.requestDate)}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.bank')}</span>
<span className="v">{detail?.bankName}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.accountNo')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.accountHolder')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('common.result')}</span>
<span className="v">{getAuthStatusText(t)(detail?.authStatus)}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.failureReason')}</span>
<span className="v">{detail?.failReason ? getAuthResultStatusText(t)(detail?.failReason) : '-' }</span>
</li>
</ul>
</div>
</div>
</div>
</main >
</>
)
}

View File

@@ -27,6 +27,8 @@ import { useExtensionAccessCheck } from '@/shared/lib/hooks/use-extension-access
import useIntersectionObserver from '@/widgets/intersection-observer';
import { snackBar } from '@/shared/lib';
import { AccountHolderSearchDetail } from '@/entities/additional-service/ui/account-holder-search/detail/account-holder-search-detail';
import { showAlert } from '@/widgets/show-alert';
import { checkGrant } from '@/shared/lib/check-grant';
export const AccountHolderSearchPage = () => {
const { navigate } = useNavigate();
@@ -176,10 +178,10 @@ export const AccountHolderSearchPage = () => {
const setDetailData = (detailData: DetailData) => {
setDetailOn(detailData.detailOn);
if(detailData.mid){
if (detailData.mid) {
setDetailMid(detailData.mid);
}
if(detailData.tid){
if (detailData.tid) {
setDetailTid(detailData.tid);
}
};
@@ -262,9 +264,9 @@ export const AccountHolderSearchPage = () => {
</div>
</div>
<AccountHolderSearchList
listItems={ listItems }
mid={ mid }
setDetailData={ setDetailData }
listItems={listItems}
mid={mid}
setDetailData={setDetailData}
></AccountHolderSearchList>
<div ref={setTarget}></div>
</div>
@@ -289,10 +291,10 @@ export const AccountHolderSearchPage = () => {
setResultStatus={setResultStatus}
></AccountHolderSearchFilter>
<AccountHolderSearchDetail
detailOn={ detailOn }
setDetailOn={ setDetailOn }
mid={ detailMid }
tid={ detailTid }
detailOn={detailOn}
setDetailOn={setDetailOn}
mid={detailMid}
tid={detailTid}
></AccountHolderSearchDetail>
<EmailBottomSheet
bottomSheetOn={emailBottomSheetOn}

View File

@@ -1,107 +0,0 @@
import moment from 'moment';
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, 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 { ExtensionAccountHolderSearchDetailParams, ExtensionAccountHolderSearchDetailResponse } from '@/entities/additional-service/model/account-holder-search/types';
import { useExtensionAccountHolderSearchDetailtMutation } from '@/entities/additional-service/api/account-holder-search/use-extension-account-holder-search-detail-mutation';
import { getAccountHolderStatusText } from '@/entities/additional-service/model/account-holder-search/constant';
import { useTranslation } from 'react-i18next';
export const AccountHolderSearchDetailPage = () => {
const { navigate } = useNavigate();
const { t } = useTranslation();
const location = useLocation();
const { mid, tid } = location.state || {};
const [detail, setDetail] = useState<ExtensionAccountHolderSearchDetailResponse>();
useSetHeaderTitle(t('additionalService.accountHolderSearch.detailTitle'));
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.additionalService.accountHolderSearch.list);
});
const { mutateAsync: accountHolderSearchDetail } = useExtensionAccountHolderSearchDetailtMutation();
const callDetail = () => {
let accountHolderSearchDetailParams: ExtensionAccountHolderSearchDetailParams = {
mid: mid,
tid: tid
}
accountHolderSearchDetail(accountHolderSearchDetailParams).then((rs: ExtensionAccountHolderSearchDetailResponse) => {
setDetail(rs);
});
};
const getDate = (date?: string) => {
return (date) ? moment(date, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '';
};
useEffect(() => {
callDetail();
}, []);
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane sub active">
<div className="pay-top">
<div className="num-amount">
<span className="amount">{detail?.accountNo}</span>
</div>
<span className="num-day">{detail?.bankName}</span>
<div className="num-day">{getDate(detail?.requestDate)}</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('transaction.sections.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('transaction.fields.accountHolder')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.accountHolderAuth.requestDate')}</span>
<span className="v">{getDate(detail?.requestDate)}</span>
</li>
<li className="kv-row">
<span className="k">{t('common.result')}</span>
<span className="v">{getAccountHolderStatusText(t)(detail?.resultStatus)}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.failureReason')}</span>
<span className="v">{detail?.failReason ? detail?.failReason : '-' }</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.bank')}</span>
<span className="v">{detail?.bankName}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.accountNo')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.accountHolderAuth.requestWay')}</span>
<span className="v">{detail?.requestWay}</span>
</li>
</ul>
</div>
</div>
</div>
</main >
</>
)
}

View File

@@ -14,6 +14,7 @@ import { useStore } from '@/shared/model/store';
import { NumericFormat } from 'react-number-format';
import { snackBar } from '@/shared/lib';
import { useTranslation } from 'react-i18next';
import { showAlert } from '@/widgets/show-alert';
export const AccountHolderSearchRequestPage = () => {
const { navigate } = useNavigate();
@@ -73,12 +74,13 @@ export const AccountHolderSearchRequestPage = () => {
snackBar(`[${t('common.failed')}] ${errorMessage}`);
}
})
.catch((error) => {
console.error("Account holder search request failed: ", error)
const errorMessage = error?.response?.data?.error?.message ||
error?.message ||
t('common.errorOccurred');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
.catch((e) => {
const errorMessage = e?.response?.data?.error?.message || e?.message ||t('common.errorOccurred');
if (e.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${errorMessage}`);
} else {
showAlert(`[${t('common.failed')}] ${errorMessage}`)
}
})
};

View File

@@ -3,7 +3,6 @@ import { SentryRoutes } from '@/shared/configs/sentry';
import { ROUTE_NAMES } from '@/shared/constants/route-names';
import { ListPage } from './list-page';
import { ArsListPage } from './ars/list-page';
import { ArsDetailPage } from './ars/detail-page';
import { ArsRequestPage } from './ars/request-page';
import { ArsRequestSuccessPage } from './ars/request-success-page';
import { KeyInPaymentPage } from './key-in-payment/key-in-payment-page';
@@ -27,8 +26,6 @@ import { PayoutRequestPage } from './payout/request-page';
import { LinkPaymentApplyPage } from './link-payment/apply/link-payment-apply-page';
import { LinkPaymentApplyConfirmPage } from './link-payment/apply/link-payment-apply-confirm-page';
import { LinkPaymentApplySuccessPage } from './link-payment/apply/link-payment-apply-success-page';
import { LinkPaymentDetailPage } from './link-payment/link-payment-detail-page';
import { LinkPaymentWaitDetailPage } from './link-payment/link-payment-wait-detail-page';
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';
@@ -63,8 +60,6 @@ export const AdditionalServicePages = () => {
<Route path={ROUTE_NAMES.additionalService.linkPayment.request} element={<LinkPaymentApplyPage />} />
<Route path={ROUTE_NAMES.additionalService.linkPayment.requestConfirm} element={<LinkPaymentApplyConfirmPage />} />
<Route path={ROUTE_NAMES.additionalService.linkPayment.confirmSuccess} element={<LinkPaymentApplySuccessPage />} />
<Route path={ROUTE_NAMES.additionalService.linkPayment.detail} element={<LinkPaymentDetailPage />} />
<Route path={ROUTE_NAMES.additionalService.linkPayment.pendingDetail} element={<LinkPaymentWaitDetailPage />} />
<Route path={ROUTE_NAMES.additionalService.linkPayment.separateApproval} element={<LinkPaymentSeparateApprovalPage />} />
</Route>
<Route path={ROUTE_NAMES.additionalService.alimtalk.base}>

View File

@@ -24,6 +24,7 @@ import { AlimTalkSettingServiceRow } from '@/entities/additional-service/ui/alim
import { useStore } from '@/shared/model/store';
import { snackBar } from '@/shared/lib';
import { useTranslation } from 'react-i18next';
import { showAlert } from '@/widgets/show-alert';
export const AlimtalkSettingPage = () => {
const { t } = useTranslation();
@@ -79,171 +80,185 @@ export const AlimtalkSettingPage = () => {
setUserVirtureAccountDepositCompleteFlag(sendUserInfo?.virtureAccountDepositCompleteFlag || false);
setUserVirtureAccountRefundFlag(sendUserInfo?.virtureAccountRefundFlag || false);
}
});
};
const callSettingSave = () => {
let params: ExtensionAlimtalkSettingSaveParams = {
mid: mid,
sendMerchantInfo: {
cardApprovalFlag: merchantCardApprovalFlag,
cardCancelFlag: merchantCardCancelFlag,
bankApprovalFlag: merchantBankApprovalFlag,
bankCancelFlag: merchantBankCancelFlag,
virtureAccountDepositRequestFlag: merchantVirtureAccountDepositRequestFlag,
virtureAccountDepositCompleteFlag: merchantVirtureAccountDepositCompleteFlag,
virtureAccountRefundFlag: merchantVirtureAccountRefundFlag
},
sendUserInfo: {
cardApprovalFlag: userCardApprovalFlag,
cardCancelFlag: userCardCancelFlag,
bankApprovalFlag: userBankApprovalFlag,
bankCancelFlag: userBankCancelFlag,
virtureAccountDepositRequestFlag: userVirtureAccountDepositRequestFlag,
virtureAccountDepositCompleteFlag: userVirtureAccountDepositCompleteFlag,
virtureAccountRefundFlag: userVirtureAccountRefundFlag
},
};
alimtalkSettingSave(params)
.then((rs) => {
snackBar(t('additionalService.alimtalk.saveSuccess'));
})
.catch((error) => {
const failReason = error?.response?.data?.message || error?.message || t('additionalService.alimtalk.unknownError');
snackBar(`[${t('common.failed')}] ${failReason}`);
});
};
useSetHeaderTitle(t('additionalService.alimtalk.title'));
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.additionalService.alimtalk.list);
});
const onClickToSave = () => {
callSettingSave();
};
// MID 초기값 설정
useEffect(() => {
if (!mid && midOptionsWithoutGids.length > 0) {
// userMid가 옵션에 있으면 userMid 사용, 없으면 첫 번째 옵션 사용
const midItem = midOptionsWithoutGids.filter((value) => value.value === userMid);
const initialMid = (midItem.length > 0) ? userMid : midOptionsWithoutGids[0]?.value || '';
if (initialMid) {
setMid(initialMid);
}).catch((e: any) => {
if (e.response?.data.error?.message) {
showAlert(e.response?.data?.error?.message);
return
}
});
};
const callSettingSave = () => {
let params: ExtensionAlimtalkSettingSaveParams = {
mid: mid,
sendMerchantInfo: {
cardApprovalFlag: merchantCardApprovalFlag,
cardCancelFlag: merchantCardCancelFlag,
bankApprovalFlag: merchantBankApprovalFlag,
bankCancelFlag: merchantBankCancelFlag,
virtureAccountDepositRequestFlag: merchantVirtureAccountDepositRequestFlag,
virtureAccountDepositCompleteFlag: merchantVirtureAccountDepositCompleteFlag,
virtureAccountRefundFlag: merchantVirtureAccountRefundFlag
},
sendUserInfo: {
cardApprovalFlag: userCardApprovalFlag,
cardCancelFlag: userCardCancelFlag,
bankApprovalFlag: userBankApprovalFlag,
bankCancelFlag: userBankCancelFlag,
virtureAccountDepositRequestFlag: userVirtureAccountDepositRequestFlag,
virtureAccountDepositCompleteFlag: userVirtureAccountDepositCompleteFlag,
virtureAccountRefundFlag: userVirtureAccountRefundFlag
},
};
alimtalkSettingSave(params)
.then((rs) => {
if (rs.status) {
snackBar(t('additionalService.alimtalk.saveSuccess'));
} else {
snackBar(`[${t('common.failed')}] ${rs.error?.message}`)
}
})
.catch((e) => {
const failReason = e?.response?.data?.message || e?.message || t('additionalService.alimtalk.unknownError');
console.log(e)
if (e.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${failReason}`);
} else {
showAlert(`[${t('common.failed')}] ${failReason}`)
}
});
};
useSetHeaderTitle(t('additionalService.alimtalk.title'));
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.additionalService.alimtalk.list);
});
const onClickToSave = () => {
callSettingSave();
};
// MID 초기값 설정
useEffect(() => {
if (!mid && midOptionsWithoutGids.length > 0) {
// userMid가 옵션에 있으면 userMid 사용, 없으면 첫 번째 옵션 사용
const midItem = midOptionsWithoutGids.filter((value) => value.value === userMid);
const initialMid = (midItem.length > 0) ? userMid : midOptionsWithoutGids[0]?.value || '';
if (initialMid) {
setMid(initialMid);
}
}, [midOptionsWithoutGids, userMid]);
}
}, [midOptionsWithoutGids, userMid]);
// mid가 설정되면 설정 정보 불러오기
useEffect(() => {
if (mid) {
callSettingDetail();
}
}, [mid]);
// mid가 설정되면 설정 정보 불러오기
useEffect(() => {
if (mid) {
callSettingDetail();
}
}, [mid]);
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane sub active">
<div className="option-list">
<div className="service-settings">
<div className="service-notice">
<div>{t('additionalService.alimtalk.settingNotice1')}</div>
<div>{t('additionalService.alimtalk.settingNotice2')}</div>
</div>
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane sub active">
<div className="option-list">
<div className="service-settings">
<div className="service-notice">
<div>{t('additionalService.alimtalk.settingNotice1')}</div>
<div>{t('additionalService.alimtalk.settingNotice2')}</div>
</div>
<div className="service-merchant">
<div className="service-title">{t('additionalService.alimtalk.merchant')}</div>
<div className="service-select">
<select
value={mid}
onChange={(e) => setMid(e.target.value)}>
{
midOptionsWithoutGids.map((value) => (
<option
key={value.value}
value={value.value}
>{value.name}</option>
))
}
</select>
<span className="ic20 arrow-down" aria-hidden="true"></span>
</div>
</div>
<div className="service-section">
<div className="service-row align-right">
<span>{t('additionalService.alimtalk.sendToMerchant')}</span>
<span>{t('additionalService.alimtalk.sendToCustomer')}</span>
</div>
</div>
<div className="service-section">
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.creditCardApproval')}
merchantFlag={merchantCardApprovalFlag}
userFlag={userCardApprovalFlag}
setMerchantFlag={setMerchantCardApprovalFlag}
setUserFlag={setUserCardApprovalFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.creditCardCancel')}
merchantFlag={merchantCardCancelFlag}
userFlag={userCardCancelFlag}
setMerchantFlag={setMerchantCardCancelFlag}
setUserFlag={setUserCardCancelFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.bankTransferApproval')}
merchantFlag={merchantBankApprovalFlag}
userFlag={userBankApprovalFlag}
setMerchantFlag={setMerchantBankApprovalFlag}
setUserFlag={setUserBankApprovalFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.bankTransferCancel')}
merchantFlag={merchantBankCancelFlag}
userFlag={userBankCancelFlag}
setMerchantFlag={setMerchantBankCancelFlag}
setUserFlag={setUserBankCancelFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.virtualAccountDepositRequest')}
merchantFlag={merchantVirtureAccountDepositRequestFlag}
userFlag={userVirtureAccountDepositRequestFlag}
setMerchantFlag={setMerchantVirtureAccountDepositRequestFlag}
setUserFlag={setUserVirtureAccountDepositRequestFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.virtualAccountDepositComplete')}
merchantFlag={merchantVirtureAccountDepositCompleteFlag}
userFlag={userVirtureAccountDepositCompleteFlag}
setMerchantFlag={setMerchantVirtureAccountDepositCompleteFlag}
setUserFlag={setUserVirtureAccountDepositCompleteFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.virtualAccountRefund')}
merchantFlag={merchantVirtureAccountRefundFlag}
userFlag={userVirtureAccountRefundFlag}
setMerchantFlag={setMerchantVirtureAccountRefundFlag}
setUserFlag={setUserVirtureAccountRefundFlag}
></AlimTalkSettingServiceRow>
<div className="service-merchant">
<div className="service-title">{t('additionalService.alimtalk.merchant')}</div>
<div className="service-select">
<select
value={mid}
onChange={(e) => setMid(e.target.value)}>
{
midOptionsWithoutGids.map((value) => (
<option
key={value.value}
value={value.value}
>{value.name}</option>
))
}
</select>
<span className="ic20 arrow-down" aria-hidden="true"></span>
</div>
</div>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToSave()}
>{t('common.save')}</button>
<div className="service-section">
<div className="service-row align-right">
<span>{t('additionalService.alimtalk.sendToMerchant')}</span>
<span>{t('additionalService.alimtalk.sendToCustomer')}</span>
</div>
</div>
<div className="service-section">
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.creditCardApproval')}
merchantFlag={merchantCardApprovalFlag}
userFlag={userCardApprovalFlag}
setMerchantFlag={setMerchantCardApprovalFlag}
setUserFlag={setUserCardApprovalFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.creditCardCancel')}
merchantFlag={merchantCardCancelFlag}
userFlag={userCardCancelFlag}
setMerchantFlag={setMerchantCardCancelFlag}
setUserFlag={setUserCardCancelFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.bankTransferApproval')}
merchantFlag={merchantBankApprovalFlag}
userFlag={userBankApprovalFlag}
setMerchantFlag={setMerchantBankApprovalFlag}
setUserFlag={setUserBankApprovalFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.bankTransferCancel')}
merchantFlag={merchantBankCancelFlag}
userFlag={userBankCancelFlag}
setMerchantFlag={setMerchantBankCancelFlag}
setUserFlag={setUserBankCancelFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.virtualAccountDepositRequest')}
merchantFlag={merchantVirtureAccountDepositRequestFlag}
userFlag={userVirtureAccountDepositRequestFlag}
setMerchantFlag={setMerchantVirtureAccountDepositRequestFlag}
setUserFlag={setUserVirtureAccountDepositRequestFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.virtualAccountDepositComplete')}
merchantFlag={merchantVirtureAccountDepositCompleteFlag}
userFlag={userVirtureAccountDepositCompleteFlag}
setMerchantFlag={setMerchantVirtureAccountDepositCompleteFlag}
setUserFlag={setUserVirtureAccountDepositCompleteFlag}
></AlimTalkSettingServiceRow>
<AlimTalkSettingServiceRow
title={t('additionalService.alimtalk.virtualAccountRefund')}
merchantFlag={merchantVirtureAccountRefundFlag}
userFlag={userVirtureAccountRefundFlag}
setMerchantFlag={setMerchantVirtureAccountRefundFlag}
setUserFlag={setUserVirtureAccountRefundFlag}
></AlimTalkSettingServiceRow>
</div>
</div>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToSave()}
>{t('common.save')}</button>
</div>
</div>
</main>
</>
);
</div>
</main>
</>
);
};

View File

@@ -1,181 +0,0 @@
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { useTranslation } from 'react-i18next';
import { HeaderType } from '@/entities/common/model/types';
import {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
import { useLocation } from 'react-router';
import { useEffect, useState } from 'react';
import { NumericFormat } from 'react-number-format';
import { useExtensionArsDetailMutation } from '@/entities/additional-service/api/ars/use-extension-ars-detail-mutation';
import {
ExtensionArsDetailParams,
ExtensionArsDetailResponse,
ExtensionArsResendParams,
ExtensionArsResendResponse,
ArsPaymentMethod,
OrderStatus
} from '@/entities/additional-service/model/ars/types';
import moment from 'moment';
import { ArsResendSmsBottomSheet } from '@/entities/additional-service/ui/ars/resend-sms-bottom-sheet';
import { useExtensionArsResendMutation } from '@/entities/additional-service/api/ars/use-extension-ars-resend-mutation';
import { getArsOrderStatusName, getArsPaymentStatusName } from '@/entities/additional-service/model/ars/constant';
import { snackBar } from '@/shared/lib';
export const ArsDetailPage = () => {
const { t, i18n } = useTranslation();
const { navigate } = useNavigate();
const location = useLocation();
const tid = location.state.tid;
const mid = location.state.mid;
const [detail, setDetail] = useState<ExtensionArsDetailResponse>();
const [bottomSheetOn, setBottomSheetOn] = useState<boolean>(false);
const { mutateAsync: extensionArsDetail } = useExtensionArsDetailMutation();
const { mutateAsync: extensionArsResend } = useExtensionArsResendMutation();
const callDetail = () => {
let params: ExtensionArsDetailParams = {
tid: tid,
mid: mid,
};
extensionArsDetail(params).then((rs: ExtensionArsDetailResponse) => {
setDetail(rs);
});
};
useSetHeaderTitle('ARS 결제 상세');
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.additionalService.ars.list);
});
useEffect(() => {
callDetail();
}, []);
const onClickToOpenResendBottomSheet = () => {
setBottomSheetOn(true);
};
const getDate = (date?: string) => {
return (date)? moment(date.substr(0, 8)).format('YYYY.MM.DD'): '';
};
const callResendSms = () => {
let params: ExtensionArsResendParams = {
mid: mid,
tid: tid
}
extensionArsResend(params).then((rs: ExtensionArsResendResponse) => {
if (rs.status) {
snackBar("SMS 재전송을 성공하였습니다.");
setBottomSheetOn(false);
callDetail(); // 상세 정보 갱신
} else {
const errorMessage = rs.error?.message || 'SMS 재전송이 실패하였습니다.';
snackBar(`[실패] ${errorMessage}`);
}
}).catch((error) => {
const errorMessage = error?.response?.data?.error?.message ||
error?.message ||
'SMS 재전송 중 오류가 발생했습니다.';
snackBar(`[실패] ${errorMessage}`);
});
};
return (
<>
<main className="full-height">
<div className="tab-content">
<div className="tab-pane sub active">
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(Number(detail?.amount) || 0) })}
</span>
</div>
<div className="num-store">{ detail?.corpName }</div>
<div className="num-day">{ getDate(detail?.paymentDate) }</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title"> </div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">MID</span>
<span className="v">{ detail?.mid }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail?.arsPaymentMethod }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ getArsPaymentStatusName(t)(detail?.paymentStatus) }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ getArsOrderStatusName(t)(detail?.orderStatus) }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{
detail?.paymentDate ? moment(detail.paymentDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'
}</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail?.goodsName }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail?.tid }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail?.buyerName }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail?.maskPhoneNumber }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail?.email }</span>
</li>
<li className="kv-row">
<span className="k"> </span>
<span className="v">{ detail?.smsVerificationCode }</span>
</li>
</ul>
</div>
{detail?.arsPaymentMethod === ArsPaymentMethod.SMS && (
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={ () => onClickToOpenResendBottomSheet() }
//disabled={ detail?.orderStatus !== OrderStatus.PENDING }
>SMS </button>
</div>
)}
</div>
</div>
</main>
<ArsResendSmsBottomSheet
setBottomSheetOn={ setBottomSheetOn }
bottomSheetOn={ bottomSheetOn }
phoneNumber={ detail?.phoneNumber }
callResendSms={ callResendSms }
></ArsResendSmsBottomSheet>
</>
);
};

View File

@@ -17,6 +17,7 @@ import { ArsRequestSuccessPage } from './request-success-page';
import { useStore } from '@/shared/model/store';
import { snackBar } from '@/shared/lib';
import { NumericFormat, PatternFormat } from 'react-number-format';
import { showAlert } from '@/widgets/show-alert';
export const ArsRequestPage = () => {
const { t } = useTranslation();
@@ -69,9 +70,13 @@ export const ArsRequestPage = () => {
snackBar(`[${t('common.failed')}] ${errorMessage}`);
}
})
.catch((error) => {
const errorMsg = error?.response?.data?.message || error?.response?.data?.error?.message || t('additionalService.ars.requestFailed');
snackBar(`[${t('common.failed')}] ${errorMsg}`);
.catch((e) => {
const errorMsg = e?.response?.data?.message || e?.response?.data?.error?.message || t('additionalService.ars.requestFailed');
if (e.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${errorMsg}`);
} else {
showAlert(`[${t('common.failed')}] ${errorMsg}`)
}
})
};

View File

@@ -1,168 +0,0 @@
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { useTranslation } from 'react-i18next';
import { HeaderType } from '@/entities/common/model/types';
import {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
import { useLocation } from 'react-router';
import { useEffect, useState } from 'react';
import { NumericFormat } from 'react-number-format';
import {
ExtensionFundAccountDownloadReceiptParams,
ExtensionFundAccountDownloadReceiptResponse,
ExtensionFundAccountResultDetailParams,
ExtensionFundAccountResultDetailResponse,
} from '@/entities/additional-service/model/fund-account/types';
import moment from 'moment';
import { useExtensionFundAccountResultDetailMutation } from '@/entities/additional-service/api/fund-account/use-extension-fund-account-result-detail-mutation';
import { useExtensionFundAccountDownloadReceiptMutation } from '@/entities/additional-service/api/fund-account/use-extension-fund-account-download-certificate-mutation';
import { EmailBottomSheet } from '@/entities/common/ui/email-bottom-sheet';
export const FundAccountResultDetailPage = () => {
const { t, i18n } = useTranslation();
const { navigate } = useNavigate();
const location = useLocation();
const tid = location.state.tid;
const mid = location.state.mid;
const [detail, setDetail] = useState<ExtensionFundAccountResultDetailResponse>();
const [emailBottomSheetOn, setEmailBottomSheetOn] = useState<boolean>(false);
const { mutateAsync: extensionFundAccountResultDetail } = useExtensionFundAccountResultDetailMutation();
const { mutateAsync: extensionFundAccountDownlaodReceipt } = useExtensionFundAccountDownloadReceiptMutation();
const callDetail = () => {
let params: ExtensionFundAccountResultDetailParams = {
mid: mid,
tid: tid,
};
extensionFundAccountResultDetail(params).then((rs: ExtensionFundAccountResultDetailResponse) => {
console.log(rs.requestDate)
setDetail(rs);
});
};
const onClickToOpenEmailBottomSheet = () => {
setEmailBottomSheetOn(true);
};
const onSendRequest = (selectedEmail?: string) => {
if (selectedEmail) {
let params: ExtensionFundAccountDownloadReceiptParams = {
mid: mid,
tid: tid,
email: selectedEmail
};
extensionFundAccountDownlaodReceipt(params).then((rs: ExtensionFundAccountDownloadReceiptResponse) => {
console.log('Receipt Download Status:', rs);
});
}
setEmailBottomSheetOn(false);
};
useSetHeaderTitle(t('additionalService.fundAccount.transferDetailTitle'));
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.additionalService.fundAccount.resultList);
});
useEffect(() => {
callDetail();
console.log(detail?.requestDate)
}, []);
return (
<>
<main className="full-height">
<div className="tab-content">
<div className="tab-pane sub active">
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(detail?.amount || 0) })}
</span>
</div>
<div className="num-store">{detail?.accountName}({detail?.accountNo})</div>
{detail?.applicationDate && (
<div className="num-day"> {moment(detail?.applicationDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</div>
)}
</div>
{/* ✅ resultMessage가 "정상"일 때만 표시 */}
{detail?.resultMessage === '정상' && (
<div className="receipt-row">
<button
type="button"
className="receipt-btn"
onClick={ onClickToOpenEmailBottomSheet }
>
<span className="icon-24 download"></span>
<span>{t('additionalService.fundAccount.depositCertificate')}</span>
</button>
</div>
)}
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.fundAccount.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.requestDateTime')}</span>
<span className="v">{moment(detail?.requestDate,'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.transferDateTime')}</span>
<span className="v">{detail?.applicationDate ? moment(detail?.applicationDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.transferResult')}</span>
<span className="v">{detail?.resultMessage || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.failureReason')}</span>
<span className="v">{detail?.failReason || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.beneficiaryName')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.bank')}</span>
<span className="v">{detail?.bankName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.accountNumber')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">MID</span>
<span className="v">{mid}</span>
</li>
<li className="kv-row">
<span className="k">{t('transaction.fields.orderNumber')}</span>
<span className="v">{detail?.moid}</span>
</li>
<li className="kv-row">
<span className="k">TID</span>
<span className="v">{detail?.tid}</span>
</li>
</ul>
</div>
</div>
</div>
</main>
<EmailBottomSheet
bottomSheetOn={emailBottomSheetOn}
setBottomSheetOn={setEmailBottomSheetOn}
imageSave={false}
sendEmail={true}
sendRequest={onSendRequest}
></EmailBottomSheet>
</>
);
};

View File

@@ -1,145 +0,0 @@
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { useTranslation } from 'react-i18next';
import { HeaderType } from '@/entities/common/model/types';
import {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
import { useLocation } from 'react-router';
import { useEffect, useState } from 'react';
import { NumericFormat } from 'react-number-format';
import { useExtensionFundAccountTransferDetailMutation } from '@/entities/additional-service/api/fund-account/use-extension-fund-account-transfer-detail-mutation';
import { ExtensionFundAccountTransferDetailParams, ExtensionFundAccountTransferDetailResponse, ExtensionFundAccountTransferRegistParams, ExtensionFundAccountTransferRequestParams, ExtensionFundAccountTransferRequestResponse, FundAccountStatus } from '@/entities/additional-service/model/fund-account/types';
import { getFundAccountStatusName } from '@/entities/additional-service/model/fund-account/constant';
import moment from 'moment';
import { snackBar } from '@/shared/lib';
import { useExtensionFundAccountTransferRequestMutation } from '@/entities/additional-service/api/fund-account/use-extension-fund-account-transfer-request-mutation';
export const FundAccountTransferDetailPage = () => {
const { t, i18n } = useTranslation();
const { navigate } = useNavigate();
const location = useLocation();
const seq = location.state.seq;
const [detail, setDetail] = useState<ExtensionFundAccountTransferDetailResponse>();
const { mutateAsync: extensionFundAccountTransferDetail } = useExtensionFundAccountTransferDetailMutation();
const { mutateAsync: extensionFundAccountTransferRequest } = useExtensionFundAccountTransferRequestMutation();
const callDetail = () => {
let params: ExtensionFundAccountTransferDetailParams = {
seq: seq
};
extensionFundAccountTransferDetail(params).then((rs: ExtensionFundAccountTransferDetailResponse) => {
setDetail(rs);
});
};
useSetHeaderTitle(t('additionalService.fundAccount.transferDetailTitle'));
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.additionalService.fundAccount.transferList);
});
useEffect(() => {
callDetail();
}, []);
const onClickToRequest = () => {
let params: ExtensionFundAccountTransferRequestParams = {
seq: seq
};
extensionFundAccountTransferRequest(params).then((rs: ExtensionFundAccountTransferRequestResponse) => {
if (rs.status) {
callDetail();
snackBar(t('additionalService.fundAccount.transferRequestSuccess'))
} else {
const errorMessage = rs.error?.message || t('additionalService.fundAccount.transferRequestFailed');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
}
}).catch((error) => {
const errorMessage = error?.response?.data?.error?.message ||
error?.message ||
t('additionalService.fundAccount.transferRequestError');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
});
};
return (
<>
<main className="full-height">
<div className="tab-content">
<div className="tab-pane sub active">
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(detail?.amount || 0) })}
</span>
</div>
<div className="num-store">{detail?.accountName}({detail?.accountNo})</div>
<div className="num-day">
{detail?.registDate ? moment(detail.registDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'}
</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.fundAccount.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.registrationDateTime')}</span>
<span className="v">
{detail?.registDate ? moment(detail.registDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss') : '-'}
</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.processingResult')}</span>
<span className="v">{getFundAccountStatusName(t)(detail?.resultStatus) || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.requestDate')}</span>
<span className="v">
{detail?.requestDate ? moment(detail.requestDate, 'YYYYMMDDHHmmss').format('YYYY.MM.DD') : '-'}
</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.transferResult')}</span>
<span className="v">{detail?.resultMessage || '-'}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.beneficiaryName')}</span>
<span className="v">{detail?.accountName}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.bank')}</span>
<span className="v">{detail?.bankCode || '-'} </span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.fundAccount.accountNumber')}</span>
<span className="v">{detail?.accountNo}</span>
</li>
<li className="kv-row">
<span className="k">MID</span>
<span className="v">{detail?.mid}</span>
</li>
</ul>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToRequest()}
disabled={detail?.resultStatus !== FundAccountStatus.REGIST_COMPLETE}
>{t('additionalService.fundAccount.transferRequestButton')}</button>
</div>
</div>
</div>
</main>
</>
);
};

View File

@@ -15,6 +15,7 @@ import { snackBar } from '@/shared/lib';
import { useExtensionFundAccountTransferRegistMutation } from '@/entities/additional-service/api/fund-account/use-extension-fund-account-transfer-regist-mutation';
import { NumericFormat } from 'react-number-format';
import { useExtensionFundAccountTransferRequestMutation } from '@/entities/additional-service/api/fund-account/use-extension-fund-account-transfer-request-mutation';
import { showAlert } from '@/widgets/show-alert';
export const FundAccountTransferRequestPage = () => {
@@ -80,11 +81,14 @@ export const FundAccountTransferRequestPage = () => {
const errorMessage = rs.error?.message || t('additionalService.fundAccount.registrationFailed');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
}
}).catch((error) => {
const errorMessage = error?.response?.data?.error?.message ||
error?.message ||
t('additionalService.fundAccount.registrationError');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
}).catch((e) => {
console.log(e)
const errorMessage = e?.response?.data?.error?.message || e?.message || t('additionalService.fundAccount.registrationError');
if (e.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${errorMessage}`);
} else {
showAlert(`[${t('common.failed')}] ${errorMessage}`)
}
});
};

View File

@@ -17,6 +17,7 @@ import { useStore } from '@/shared/model/store';
import { snackBar } from '@/shared/lib';
import { NumericFormat, PatternFormat } from 'react-number-format';
import { useTranslation } from 'react-i18next';
import { showAlert } from '@/widgets/show-alert';
export const KeyInPaymentRequestPage = () => {
const { t } = useTranslation();
@@ -94,21 +95,25 @@ export const KeyInPaymentRequestPage = () => {
} else {
// 실패: 화면 유지 & 입력 내용 유지
const errorMessage = rs.data?.resultMessage ||
rs.error?.message ||
rs.resultMessage||
t('additionalService.keyIn.requestFailed');
rs.error?.message ||
rs.resultMessage ||
t('additionalService.keyIn.requestFailed');
console.log('최종 errorMessage:', errorMessage);
// HTML 태그 제거
const cleanMessage = errorMessage.replace(/<br\s*\/?>/gi, ' ').trim();
snackBar(`[${t('common.failed')}] ${cleanMessage}`);
}
}).catch((error) => {
console.error('결제 실패:', error);
console.error(error);
const errorMessage = error?.response?.data?.data?.resultMessage ||
error?.response?.data?.error?.message ||
error?.message ||
t('additionalService.keyIn.requestError');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
error?.response?.data?.error?.message ||
error?.message ||
t('additionalService.keyIn.requestError');
if (error.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${errorMessage}`);
} else {
showAlert(`[${t('common.failed')}] ${errorMessage}`)
}
});
};
@@ -199,8 +204,8 @@ export const KeyInPaymentRequestPage = () => {
allowNegative={false}
displayType="input"
onValueChange={(values) => {
const { floatValue} = values;
setAmount( floatValue ?? 0);
const { floatValue } = values;
setAmount(floatValue ?? 0);
}}
></NumericFormat>
</div>

View File

@@ -8,6 +8,7 @@ import { PATHS } from '@/shared/constants/paths';
import { useExtensionLinkPayRequestMutation } from '@/entities/additional-service/api/link-payment/use-extension-link-pay-request-mutation';
import { ExtensionLinkPayRequestParams, ExtensionLinkPayRequestResponse, LinkPaymentFormData } from '@/entities/additional-service/model/link-pay/types';
import { snackBar } from '@/shared/lib';
import { showAlert } from '@/widgets/show-alert';
export const LinkPaymentApplyConfirmPage = () => {
const { t } = useTranslation();
@@ -60,11 +61,14 @@ export const LinkPaymentApplyConfirmPage = () => {
}
})
.catch((error) => {
// 네트워크 에러 등 예외 상황
const errorMessage = error?.response?.data?.error?.message ||
error?.message ||
t('additionalService.linkPayment.requestError');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
if (error.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${errorMessage}`);
} else {
showAlert(`[${t('common.failed')}] ${errorMessage}`)
}
});
};

View File

@@ -1,179 +0,0 @@
import { useEffect, useState } from 'react';
import { PATHS } from '@/shared/constants/paths';
import { useLocation } from 'react-router';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { HeaderType } from '@/entities/common/model/types';
import {
useSetOnBack,
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode
} from '@/widgets/sub-layout/use-sub-layout';
import { overlay } from 'overlay-kit';
import { Dialog } from '@/shared/ui/dialogs/dialog';
import { useExtensionLinkPayHistoryDetailMutation } from '@/entities/additional-service/api/link-payment/use-extension-link-pay-history-detail-mutation';
import { AdditionalServiceCategory, DetailInfo, DetailResponse, PaymentInfo, TitleInfo } from '@/entities/additional-service/model/types';
import { TitleInfoWrap } from '@/entities/additional-service/ui/info-wrap/title-info-wrap';
import { PaymentInfoWrap } from '@/entities/additional-service/ui/info-wrap/payment-info-wrap';
import { DetailInfoWrap } from '@/entities/additional-service/ui/info-wrap/detail-info-wrap';
import { useExtensionLinkPayHistoryResendMutation } from '@/entities/additional-service/api/link-payment/use-extension-link-pay-history-resend-mutation';
import { ExtensionLinkPayHistoryDetailParams, ExtensionLinkPayHistoryResendParams } from '@/entities/additional-service/model/link-pay/types';
import { snackBar } from '@/shared/lib';
import moment from 'moment';
import { useTranslation } from 'react-i18next';
export const LinkPaymentDetailPage = () => {
const { t } = useTranslation();
const { navigate } = useNavigate();
const location = useLocation();
const { mid, tid, requestId, subReqId } = location.state || {};
const [titleInfo, setTitleInfo] = useState<TitleInfo>();
const [detailInfo, setDetailInfo] = useState<DetailInfo>();
const [paymentInfo, setPaymentInfo] = useState<PaymentInfo>();
const [detailExposure, setDetailExposure] = useState<boolean>(false);
const [showPayment, setShowPayment] = useState<boolean>(false);
useSetHeaderTitle(t('additionalService.linkPayment.detailTitle'));
useSetHeaderType(HeaderType.RightClose);
useSetOnBack(() => {
navigate(-1); // Go back using browser history
});
useSetFooterMode(false);
const { mutateAsync: linkPayHistoryDetail } = useExtensionLinkPayHistoryDetailMutation();
const { mutateAsync: linkPayHistoryResend } = useExtensionLinkPayHistoryResendMutation();
// Query detail information
const callDetail = () => {
let detailParam: ExtensionLinkPayHistoryDetailParams = {
mid: mid,
requestId: requestId,
subReqId: subReqId
}
linkPayHistoryDetail(detailParam).then((rs: DetailResponse) => {
console.log("Detail Info: ", rs)
setTitleInfo(rs.titleInfo)
setDetailInfo(rs.detailInfo)
setPaymentInfo(rs.paymentInfo)
setDetailExposure(rs.detailExposure ?? false)
})
}
// Resend API
const resendPayment = () => {
let resendParam: ExtensionLinkPayHistoryResendParams = {
mid: mid,
requestId: requestId,
sendMethod: paymentInfo?.sendMethod
}
linkPayHistoryResend(resendParam)
.then((response) => {
if (response.status) {
snackBar(t('additionalService.linkPayment.resendSuccess'));
callDetail();
} else {
const errorMessage = response.error?.message || t('additionalService.linkPayment.resendFailed');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
}
})
.catch((error) => {
const errorMessage = error?.response?.data?.error?.message ||
error?.message ||
t('additionalService.linkPayment.resendError');
snackBar(`[${t('common.failed')}] ${errorMessage}`);
});
}
const onClickToResend = () => {
let msg = t('additionalService.linkPayment.resendConfirm');
overlay.open(({
isOpen,
close,
unmount
}) => {
return (
<Dialog
afterLeave={unmount}
open={isOpen}
onClose={close}
onConfirmClick={() => resendPayment()}
message={msg}
buttonLabel={[t('common.cancel'), t('common.confirm')]}
/>
);
});
};
const onClickToSeparateApproval = () => {
navigate(PATHS.additionalService.linkPayment.separateApproval, {
state: { mid, requestId }
});
};
// Check if resend button should be enabled
const isResendEnabled = () => {
// paymentStatus must be "ACTIVE"
if (paymentInfo?.paymentStatus !== 'ACTIVE') {
return false;
}
// paymentLimitDate must not have passed today's date
if (paymentInfo?.paymentLimitDate) {
const limitDate = moment(paymentInfo.paymentLimitDate, 'YYYYMMDD');
const today = moment().startOf('day');
return limitDate.isSameOrAfter(today);
}
return false;
};
useEffect(() => {
callDetail();
}, []);
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane sub active">
<div className="pay-top">
<TitleInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentHistory}
titleInfo={titleInfo}
></TitleInfoWrap>
</div>
<div className="pay-detail">
<div className="detail-divider"></div>
<PaymentInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentHistory}
paymentInfo={paymentInfo}
></PaymentInfoWrap>
<div className="detail-divider"></div>
<DetailInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentHistory}
detailInfo={detailInfo}
></DetailInfoWrap>
<div className="detail-divider"></div>
<div className="link-payment-detail-button" style={{ paddingBottom: '85px' }}>
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToSeparateApproval()}
disabled={detailExposure}
>{t('additionalService.linkPayment.separateApprovalDetail')}</button>
</div>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToResend()}
disabled={!isResendEnabled()}
>{t('additionalService.linkPayment.resend')}</button>
</div>
</div>
</div>
</main >
</>
)
};

View File

@@ -1,124 +0,0 @@
import { useEffect, useState } from 'react';
import { PATHS } from '@/shared/constants/paths';
import { useLocation } from 'react-router';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { HeaderType } from '@/entities/common/model/types';
import {
useSetOnBack,
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode
} from '@/widgets/sub-layout/use-sub-layout';
import { overlay } from 'overlay-kit';
import { Dialog } from '@/shared/ui/dialogs/dialog';
import { TitleInfoWrap } from '@/entities/additional-service/ui/info-wrap/title-info-wrap';
import { AdditionalServiceCategory, DetailResponse, PaymentInfo, TitleInfo } from '@/entities/additional-service/model/types';
import { useExtensionLinkPayWaitDetailMutation, } from '@/entities/additional-service/api/link-payment/use-extension-link-pay-wait-detail-mutation';
import { PaymentInfoWrap } from '@/entities/additional-service/ui/info-wrap/payment-info-wrap';
import { useExtensionLinkPayWaitDeleteMutation } from '@/entities/additional-service/api/link-payment/use-extension-link-pay-wait-delete-mutation';
import { ExtensionLinkPayWaitDeleteParams, ExtensionLinkPayWaitDetailParams, LinkPaymentProcessStatus } from '@/entities/additional-service/model/link-pay/types';
import { snackBar } from '@/shared/lib';
import { useTranslation } from 'react-i18next';
export const LinkPaymentWaitDetailPage = () => {
const { t } = useTranslation();
const { navigate } = useNavigate();
const location = useLocation();
const { mid, requestId } = location.state || {};
const [titleInfo, setTitleInfo] = useState<TitleInfo>();
const [paymentInfo, setPaymentInfo] = useState<PaymentInfo>();
useSetHeaderTitle(t('additionalService.linkPayment.waitDetailTitle'));
useSetHeaderType(HeaderType.RightClose);
useSetOnBack(() => {
navigate(PATHS.additionalService.linkPayment.pendingSend);
});
useSetFooterMode(false);
const { mutateAsync: linkPayWaitDetail } = useExtensionLinkPayWaitDetailMutation();
const { mutateAsync: linkPayWaitDelete } = useExtensionLinkPayWaitDeleteMutation();
const callDetail = () => {
let detailParam: ExtensionLinkPayWaitDetailParams = {
mid: mid,
requestId: requestId
}
linkPayWaitDetail(detailParam).then((rs: DetailResponse) => {
setTitleInfo(rs.titleInfo)
setPaymentInfo(rs.paymentInfo)
})
}
const deletePayment = () => {
let deleteParam: ExtensionLinkPayWaitDeleteParams = {
mid: mid,
requestId: requestId
}
linkPayWaitDelete(deleteParam)
.then((rs) => {
callDetail();
snackBar(t('additionalService.linkPayment.deleteSuccess'))
})
.catch((error) => {
snackBar(`[${t('common.failed')}] ${error?.response?.data?.message}`)
});
}
const onClickToCancel = () => {
let msg = t('additionalService.linkPayment.deleteConfirm');
overlay.open(({
isOpen,
close,
unmount
}) => {
return (
<Dialog
afterLeave={unmount}
open={isOpen}
onClose={close}
onConfirmClick={() => deletePayment()}
message={msg}
buttonLabel={[t('common.cancel'), t('common.confirm')]}
/>
);
});
};
useEffect(() => {
callDetail();
}, []);
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane sub active">
<div className="pay-top">
<TitleInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentWait}
titleInfo={titleInfo}
></TitleInfoWrap>
</div>
<div className="pay-detail">
<div className="detail-divider"></div>
<PaymentInfoWrap
additionalServiceCategory={AdditionalServiceCategory.LinkPaymentWait}
paymentInfo={paymentInfo}
></PaymentInfoWrap>
</div>
</div>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={() => onClickToCancel()}
disabled={paymentInfo?.processStatus !== LinkPaymentProcessStatus.SEND_REQUEST}
>{t('additionalService.linkPayment.delete')}</button>
</div>
</div>
</main >
</>
)
};

View File

@@ -1,190 +0,0 @@
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { useTranslation } from 'react-i18next';
import { HeaderType } from '@/entities/common/model/types';
import {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
import { useExtensionPayoutDetailMutation } from '@/entities/additional-service/api/payout/use-extension-payout-detail-mutation';
import { useLocation } from 'react-router';
import { ExtensionPayoutDetailDownloadCertificateParams, ExtensionPayoutDetailDownloadCertificateResponse, ExtensionPayoutDetailParams, ExtensionPayoutDetailResponse } from '@/entities/additional-service/model/payout/types';
import { useEffect, useState } from 'react';
import { NumericFormat } from 'react-number-format';
import { useExtensionPayoutDetailDownloadCertificateMutation } from '@/entities/additional-service/api/payout/use-extension-payout-detail-download-cetificate-mutation';
import moment from 'moment';
import { EmailBottomSheet } from '@/entities/common/ui/email-bottom-sheet';
import { DownloadTypeBottomSheet } from '@/entities/common/ui/download-type-bottom-sheet';
export const PayoutDetailPage = () => {
const { t, i18n } = useTranslation();
const { navigate } = useNavigate();
const location = useLocation();
const tid = location.state.tid;
const mid = location.state.mid;
const [detail, setDetail] = useState<ExtensionPayoutDetailResponse>();
const [downloadTypeBottomSheetOn, setDownloadTypeBottomSheetOn] = useState<boolean>(false);
const [emailBottomSheetOn, setEmailBottomSheetOn] = useState<boolean>(false);
const { mutateAsync: extensionPayoutDetail } = useExtensionPayoutDetailMutation();
const { mutateAsync: extensionPayoutDetailDownloadCertification } = useExtensionPayoutDetailDownloadCertificateMutation();
const callDetail = () => {
let params: ExtensionPayoutDetailParams = {
tid: tid,
mid: mid,
};
extensionPayoutDetail(params).then((rs: ExtensionPayoutDetailResponse) => {
setDetail(rs);
});
};
useSetHeaderTitle(t('additionalService.payout.detailTitle'));
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.additionalService.payout.list);
});
const onClickToDownload = () => {
setDownloadTypeBottomSheetOn(true);
};
const onSelectDownloadType = (type: 'IMAGE' | 'EMAIL') => {
if (type === 'IMAGE') {
// Save image directly
const params: ExtensionPayoutDetailDownloadCertificateParams = {
mid: mid,
tid: tid,
requestType: 'IMAGE',
email: ''
};
extensionPayoutDetailDownloadCertification(params)
.then((rs: ExtensionPayoutDetailDownloadCertificateResponse) => {
console.log('Certificate Download Status:', rs);
})
.catch((error) => {
console.error('Certificate Download Failed:', error);
});
} else {
// Open EmailBottomSheet for email option
setEmailBottomSheetOn(true);
}
};
const onSendRequest = (selectedEmail?: string) => {
if (selectedEmail) {
const params: ExtensionPayoutDetailDownloadCertificateParams = {
mid: mid,
tid: tid,
requestType: 'EMAIL',
email: selectedEmail
};
extensionPayoutDetailDownloadCertification(params)
.then((rs: ExtensionPayoutDetailDownloadCertificateResponse) => {
console.log('Certificate Download Status:', rs);
})
.catch((error) => {
console.error('Certificate Download Failed:', error);
});
}
setEmailBottomSheetOn(false);
};
useEffect(() => {
callDetail();
}, []);
return (
<>
<main className="full-height">
<div className="tab-content">
<div className="tab-pane sub active">
<div className="pay-top">
<div className="num-amount">
<span className="amount">
{t('home.money', { value: new Intl.NumberFormat('en-US').format(detail?.disbursementAmount || 0) })}
</span>
</div>
<div className="num-store">{detail?.companyName}</div>
<div className="num-day">{detail?.settlementDate}</div>
<div className="receipt-row">
<button
className="receipt-btn"
type="button"
onClick={ onClickToDownload }
>
<span className="icon-24 download"></span>
<span>{t('additionalService.payout.depositCertificate')}</span>
</button>
</div>
</div>
<div className="detail-divider"></div>
<div className="pay-detail">
<div className="detail-title">{t('additionalService.payout.detailInfo')}</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k">{t('additionalService.payout.disbursementStatus')}</span>
<span className="v">{ detail?.disbursementStatus }</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.transactionType')}</span>
<span className="v">{ detail?.transTypeName }</span>
</li>
<li className="kv-row">
<span className="k">{t('common.requestDate')}</span>
<span className="v">{ moment(detail?.requestDate).format('YYYY.MM.DD') }</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.disbursementDateTime')}</span>
<span className="v">{moment(detail?.settlementDateTime,'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.businessNumber')}</span>
<span className="v">{ detail?.companyNo }</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.accountHolder')}</span>
<span className="v">{ detail?.accountName }</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.bank')}</span>
<span className="v">{ detail?.bankName }</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.accountNumber')}</span>
<span className="v">{ detail?.accountNo }</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.depositor')}</span>
<span className="v">{ detail?.depositName }</span>
</li>
<li className="kv-row">
<span className="k">{t('additionalService.payout.failureReason')}</span>
<span className="v">{ detail?.failReason }</span>
</li>
</ul>
</div>
</div>
</div>
</main>
<DownloadTypeBottomSheet
bottomSheetOn={downloadTypeBottomSheetOn}
setBottomSheetOn={setDownloadTypeBottomSheetOn}
onSelectType={onSelectDownloadType}
/>
<EmailBottomSheet
bottomSheetOn={emailBottomSheetOn}
setBottomSheetOn={setEmailBottomSheetOn}
imageSave={false}
sendEmail={true}
sendRequest={onSendRequest}
/>
</>
);
};

View File

@@ -17,6 +17,7 @@ import moment from 'moment';
import { NumericFormat } from "react-number-format";
import { snackBar } from "@/shared/lib";
import { useTranslation } from 'react-i18next';
import { showAlert } from "@/widgets/show-alert";
export const PayoutRequestPage = () => {
const { t } = useTranslation();
@@ -53,10 +54,15 @@ export const PayoutRequestPage = () => {
snackBar(`[${t('common.failed')}] ${rs.error?.message}`)
}
})
.catch((error) => {
snackBar(`[${t('common.failed')}] ${error?.response?.data?.message} ` || `[${t('common.failed')}] ${t('additionalService.payout.requestFailed')}`)
})
;
.catch((e) => {
console.log(e)
if (e.response?.data?.error?.root !== "SystemErrorCode") {
snackBar(`[${t('common.failed')}] ${e.response?.data?.error?.message}`)
} else {
showAlert(`[${t('common.failed')}] ${e?.response?.data?.error?.message} ` || `[${t('common.failed')}] ${t('additionalService.payout.requestFailed')}`);
return;
}
});
};
const isFormValid = () => {