- 부가서비스 소개 List, 권한 검증 추가

- 각 API 오류 수정
This commit is contained in:
HyeonJongKim
2025-10-24 20:45:24 +09:00
parent 2f13c29d1b
commit b10376e171
33 changed files with 657 additions and 284 deletions

View File

@@ -13,6 +13,9 @@ import { ExtensionPayoutDetailDownloadCertificateParams, ExtensionPayoutDetailDo
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 { navigate } = useNavigate();
@@ -21,9 +24,9 @@ export const PayoutDetailPage = () => {
const tid = location.state.tid;
const mid = location.state.mid;
const [requestType, setRequestType] = useState<string>('');
const [email, setEmail] = useState<string>('');
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();
@@ -47,15 +50,48 @@ export const PayoutDetailPage = () => {
});
const onClickToDownload = () => {
let params: ExtensionPayoutDetailDownloadCertificateParams = {
tid: tid,
mid: mid,
requestType: requestType,
email: email
};
extensionPayoutDetailDownloadCertification(params).then((rs: ExtensionPayoutDetailDownloadCertificateResponse) => {
console.log(rs);
});
setDownloadTypeBottomSheetOn(true);
};
const onSelectDownloadType = (type: 'IMAGE' | 'EMAIL') => {
if (type === 'IMAGE') {
// 이미지 저장은 바로 실행
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 {
// 이메일은 EmailBottomSheet 열기
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(() => {
@@ -105,11 +141,11 @@ export const PayoutDetailPage = () => {
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail?.requestDate }</span>
<span className="v">{ moment(detail?.requestDate).format('YYYY.MM.DD') }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail?.settlementDateTime }</span>
<span className="v">{moment(detail?.settlementDateTime,'YYYYMMDDHHmmss').format('YYYY.MM.DD HH:mm:ss')}</span>
</li>
<li className="kv-row">
<span className="k"></span>
@@ -140,6 +176,18 @@ export const PayoutDetailPage = () => {
</div>
</div>
</main>
<DownloadTypeBottomSheet
bottomSheetOn={downloadTypeBottomSheetOn}
setBottomSheetOn={setDownloadTypeBottomSheetOn}
onSelectType={onSelectDownloadType}
/>
<EmailBottomSheet
bottomSheetOn={emailBottomSheetOn}
setBottomSheetOn={setEmailBottomSheetOn}
imageSave={false}
sendEmail={true}
sendRequest={onSendRequest}
/>
</>
);
};

View File

@@ -15,9 +15,9 @@ import {
import { JSX, useEffect, useState } from 'react';
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constant';
import {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode,
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
import moment from 'moment';
@@ -29,8 +29,13 @@ import { ListDateGroup } from '@/entities/additional-service/ui/list-date-group'
import { AdditionalServiceCategory } from '@/entities/additional-service/model/types';
import { useStore } from '@/shared/model/store';
import { EmailBottomSheet } from '@/entities/common/ui/email-bottom-sheet';
import { useExtensionAccessCheck } from '@/shared/lib/hooks/use-extension-access-check';
export const PayoutListPage = () => {
// 권한 체크
const { hasAccess, AccessDeniedDialog } = useExtensionAccessCheck({
extensionCode: 'PAYOUT'
});
const { navigate } = useNavigate();
const userMid = useStore.getState().UserStore.mid;
@@ -44,12 +49,12 @@ export const PayoutListPage = () => {
const [toDate, setToDate] = useState<string>(moment().format('YYYYMMDD'));
const [status, setStatus] = useState<PayoutDisbursementStatus>(PayoutDisbursementStatus.ALL);
const [minAmount, setMinAmount] = useState<number>(0);
const [maxAmount, setMaxAmount] = useState<number>(50000000);
const [maxAmount, setMaxAmount] = useState<number>(50000000);
const [emailBottomSheetOn, setEmailBottomSheetOn] = useState<boolean>(false);
const { mutateAsync: extensionPayoutList } = useExtensionPayoutListMutation();
const { mutateAsync: extensionPayoutExcel } = useExtensionPayoutExcelMutation();
useSetHeaderTitle('지급대행');
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
@@ -65,26 +70,19 @@ export const PayoutListPage = () => {
sortType?: SortTypeKeys,
status?: PayoutDisbursementStatus
}) => {
let newMinAmount = minAmount;
if(!!minAmount && typeof(minAmount) === 'string'){
newMinAmount = parseInt(minAmount);
}
let newMaxAmount = maxAmount;
if(!!maxAmount && typeof(maxAmount) === 'string'){
newMaxAmount = parseInt(maxAmount);
}
let params: ExtensionPayoutListParams = {
mid: mid,
searchDateType: searchDateType,
fromDate: fromDate,
toDate: toDate,
status: option?.status ?? status,
minAmount: newMinAmount,
maxAmount: newMaxAmount,
minAmount: minAmount,
maxAmount: maxAmount,
page: pageParam
};
if(params.page){
if (params.page) {
params.page.sortType = option?.sortType || sortType;
setPageParam(params.page);
}
@@ -102,13 +100,9 @@ export const PayoutListPage = () => {
if (selectedEmail) {
const params: ExtensionPayoutExcelParams = {
mid: mid,
searchDateType: searchDateType,
email: selectedEmail,
fromDate: fromDate,
toDate: toDate,
status: status,
minAmount: minAmount,
maxAmount: maxAmount,
//email: selectedEmail
};
extensionPayoutExcel(params).then((rs: ExtensionPayoutExcelResponse) => {
console.log('Excel Download Status:', rs);
@@ -127,9 +121,6 @@ export const PayoutListPage = () => {
};
const onClickToDisbursementStatus = (val: PayoutDisbursementStatus) => {
setStatus(val);
callExtensionPayoutList({
status: val
});
};
useEffect(() => {
@@ -145,9 +136,9 @@ export const PayoutListPage = () => {
]);
const getListDateGroup = () => {
let rs= [];
let rs = [];
let date = '';
let list= [];
let list = [];
for (let i = 0; i < listItems.length; i++) {
let itemDateStr = '';
if (searchDateType === PayoutSearchDateType.REQUEST_DATE) {
@@ -190,6 +181,10 @@ export const PayoutListPage = () => {
return rs;
};
if (!hasAccess) {
return <AccessDeniedDialog />;
}
return (
<>
<main>
@@ -198,19 +193,19 @@ export const PayoutListPage = () => {
<section className="summary-section">
<div className="credit-controls">
<div>
<input
<input
className="credit-period"
type="text"
value={ moment(fromDate).format('YYYY.MM.DD') + '-' + moment(toDate).format('YYYY.MM.DD') }
readOnly={ true }
value={moment(fromDate).format('YYYY.MM.DD') + '-' + moment(toDate).format('YYYY.MM.DD')}
readOnly={true}
/>
<button
className="filter-btn"
<button
className="filter-btn"
aria-label="필터"
onClick={ () => onClickToOpenFilter() }
onClick={() => onClickToOpenFilter()}
>
<img
src={ IMAGE_ROOT + '/ico_setting.svg' }
<img
src={IMAGE_ROOT + '/ico_setting.svg'}
alt="검색옵션"
/>
</button>
@@ -218,10 +213,10 @@ export const PayoutListPage = () => {
<button
className="download-btn"
aria-label="다운로드"
onClick={ () => onClickToOpenEmailBottomSheet() }
onClick={() => onClickToOpenEmailBottomSheet()}
>
<img
src={ IMAGE_ROOT + '/ico_download.svg' }
src={IMAGE_ROOT + '/ico_download.svg'}
alt="다운로드"
/>
</button>
@@ -240,52 +235,52 @@ export const PayoutListPage = () => {
<section className="filter-section">
<SortTypeBox
sortType={ sortType }
onClickToSort={ onClickToSort }
sortType={sortType}
onClickToSort={onClickToSort}
></SortTypeBox>
<div className="excrow mr-0">
<div className="full-menu-keywords no-padding">
{
PayoutDisbursementStatusBtnGroup.map((value, index) => (
<span
key={ `key-service-code=${ index }` }
className={ `keyword-tag ${(status === value.value)? 'active': ''}` }
onClick={ () => onClickToDisbursementStatus(value.value) }
>{ value.name }</span>
))
}
{
PayoutDisbursementStatusBtnGroup.map((value, index) => (
<span
key={`key-service-code=${index}`}
className={`keyword-tag ${(status === value.value) ? 'active' : ''}`}
onClick={() => onClickToDisbursementStatus(value.value)}
>{value.name}</span>
))
}
</div>
</div>
</section>
<section className="transaction-list">
{ getListDateGroup() }
{getListDateGroup()}
</section>
<div className="apply-row">
<button
className="btn-50 btn-blue flex-1"
onClick={ () => onClickToNavigation() }
onClick={() => onClickToNavigation()}
> </button>
</div>
</div>
</div>
</main>
<PayoutFilter
filterOn={ filterOn }
setFilterOn={ setFilterOn }
mid={ mid }
searchDateType={ searchDateType }
fromDate={ fromDate }
toDate={ toDate }
status= { status }
minAmount={ minAmount }
maxAmount={ maxAmount }
setMid={ setMid }
setSearchDateType={ setSearchDateType }
setFromDate={ setFromDate }
setToDate={ setToDate }
setStatus={ setStatus }
setMinAmount={ setMinAmount }
setMaxAmount={ setMaxAmount }
filterOn={filterOn}
setFilterOn={setFilterOn}
mid={mid}
searchDateType={searchDateType}
fromDate={fromDate}
toDate={toDate}
status={status}
minAmount={minAmount}
maxAmount={maxAmount}
setMid={setMid}
setSearchDateType={setSearchDateType}
setFromDate={setFromDate}
setToDate={setToDate}
setStatus={setStatus}
setMinAmount={setMinAmount}
setMaxAmount={setMaxAmount}
></PayoutFilter>
<EmailBottomSheet
bottomSheetOn={emailBottomSheetOn}

View File

@@ -8,12 +8,14 @@ import {
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
import { useState } from "react";
import { ChangeEvent, useState } from "react";
import { useExtensionPayoutRequestMutation } from "@/entities/additional-service/api/payout/use-extension-payout-request-mutation";
import { ExtensionPayoutRequestParams, ExtensionPayoutRequestResponse } from "@/entities/additional-service/model/payout/types";
import NiceCalendar from "@/shared/ui/calendar/nice-calendar";
import { useStore } from "@/shared/model/store";
import moment from 'moment';
import { NumericFormat } from "react-number-format";
import { snackBar } from "@/shared/lib";
export const PayoutRequestPage = () => {
const { navigate } = useNavigate();
@@ -41,9 +43,14 @@ export const PayoutRequestPage = () => {
disbursementAmount: disbursementAmount,
settlementDate: settlementDate,
};
extensionPayoutRequest(params).then((rs: ExtensionPayoutRequestResponse) => {
navigate(PATHS.additionalService.payout.list);
});
extensionPayoutRequest(params)
.then((rs) => {
snackBar("신청을 성공하였습니다.")
})
.catch((error) => {
snackBar(`[실패] ${error?.response?.data?.message} `|| '[실패] 신청을 실패하였습니다.')
})
;
};
const isFormValid = () => {
@@ -58,9 +65,8 @@ export const PayoutRequestPage = () => {
setSettlementDate(moment(date).format('YYYYMMDD'));
setCalendarOpen(false);
};
const onClickToOpenCalendar = () => {
setCalendarOpen(true);
};
return (
<>
<main>
@@ -69,44 +75,44 @@ export const PayoutRequestPage = () => {
<div className="ing-list">
<div className="billing-form gap-30">
<div className="billing-row">
<div className="billing-label">ID<span>*</span></div>
<div className="billing-label">ID</div>
<div className="billing-field">
<input
type="text"
value={submallId}
onChange={(e) => setSubmallId(e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => setSubmallId(e.target.value)}
/>
</div>
</div>
<div className="billing-row">
<div className="billing-label"><span>*</span></div>
<div className="billing-label"></div>
<div className="billing-field">
<input
type="text"
<NumericFormat
value={disbursementAmount}
onChange={(e) => setDisbursementAmount(parseInt(e.target.value))}
/>
allowNegative={false}
displayType="input"
onChange={(e: ChangeEvent<HTMLInputElement>) => setDisbursementAmount(parseInt(e.target.value))}
></NumericFormat>
</div>
</div>
<div className="billing-row">
<div className="billing-label"><span>*</span></div>
<div className="billing-label"></div>
<div className="billing-field">
<div className="input-wrapper date">
<div className="input-wrapper date wid-100">
<input
className="date-input"
type="text"
placeholder="날짜 선택"
value={settlementDate}
value={settlementDate ? moment(settlementDate).format('YYYY.MM.DD') : '' }
readOnly={true}
/>
<button
className="date-btn"
type="button"
onClick={() => onClickToOpenCalendar()}
disabled={!isFormValid}
onClick={() => setCalendarOpen(true)}
>
<img
src={IMAGE_ROOT + '/ico_date.svg'}
alt="날짜 선택"
alt="clear"
/>
</button>
</div>
@@ -129,6 +135,7 @@ export const PayoutRequestPage = () => {
setCalendarOpen={setCalendarOpen}
calendarType={CalendarType.Single}
setNewDate={setNewDate}
minDate={new Date()}
></NiceCalendar>
</>
);