부가서비스

- 링크결제 결제신청 API 연결
This commit is contained in:
HyeonJongKim
2025-09-22 17:00:02 +09:00
parent ea9803d442
commit b5bdd3d855
9 changed files with 327 additions and 98 deletions

View File

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

View File

@@ -13,7 +13,7 @@ import { ExtensionLinkPayWaitDeleteParams } from '../../model/types';
export const extensionLinkPayWaitDelete = async (params: ExtensionLinkPayWaitDeleteParams)=> { export const extensionLinkPayWaitDelete = async (params: ExtensionLinkPayWaitDeleteParams)=> {
return resultify( return resultify(
axios.post<ExtensionLinkPayWaitDeleteRespone>(API_URL_ADDITIONAL_SERVICE.extensionLinkPayMentWaitDelete(), params) axios.post<ExtensionLinkPayWaitDeleteRespone>(API_URL_ADDITIONAL_SERVICE.extensionLinkPaymentWaitDelete(), params)
); );
}; };

View File

@@ -215,6 +215,21 @@ export enum LinkPaymentSendingStatus {
SEND_CANCEL = "SEND_CANCEL" SEND_CANCEL = "SEND_CANCEL"
} }
export enum IdentityType {
INDIVIDUAL = "INDIVIDUAL",
CORPORATE = "CORPORATE"
}
export enum Language {
KR = "KR",
EN = "EN"
}
export enum LinkContentType {
BASIC = "BASIC",
ADDITIONAL = "ADDITIONAL"
}
export interface LinkPaymentHistoryListItem { export interface LinkPaymentHistoryListItem {
tid?: string; tid?: string;
// TODO : buyerName 필요 // TODO : buyerName 필요
@@ -396,6 +411,45 @@ export interface ExtensionLinkPayHistoryDownloadExcelRespone {
status: boolean; status: boolean;
} }
export interface ExtensionLinkPayRequestParams extends ExtensionRequestParams {
sendMethod: string;
goodsName: string;
amount: number;
moid: string;
paymentExpiryDate: string;
buyerName: string;
email: string;
phoneNumber: string;
isIdentity: boolean;
identityType: IdentityType;
identityValue: string;
language: Language;
linkContentType: LinkContentType;
}
export interface ExtensionLinkPayRequestResponse {
status: boolean
}
export interface LinkPaymentFormData {
// Step 1
mid: string;
sendMethod: LinkPaymentSendMethod;
goodsName: string;
amount: number;
moid: string;
paymentExpiryDate: string;
// Step 2
buyerName: string;
email: string;
phoneNumber: string;
isIdentity: boolean;
identityType: IdentityType;
identityValue: string;
language: Language;
linkContentType: LinkContentType;
}
export interface ExtensionLinkPayHistoryDetailParams extends ExtensionRequestParams { export interface ExtensionLinkPayHistoryDetailParams extends ExtensionRequestParams {
tid: string; tid: string;
} }

View File

@@ -1,33 +1,31 @@
import { useState } from 'react';
import { PATHS } from '@/shared/constants/paths'; import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate'; import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { useSetOnBack } from '@/widgets/sub-layout/use-sub-layout'; import { useSetOnBack } from '@/widgets/sub-layout/use-sub-layout';
import { IMAGE_ROOT } from '@/shared/constants/common'; import { LinkPaymentFormData, LinkPaymentSendMethod } from '@/entities/additional-service/model/types';
import { SingleDatePicker } from '@/shared/ui/filter/single-date-picker';
export const LinkPaymentStep1 = () => { interface LinkPaymentStep1Props {
formData: LinkPaymentFormData;
setFormData: (formData: LinkPaymentFormData) => void;
}
export const LinkPaymentStep1 = ({ formData, setFormData }: LinkPaymentStep1Props) => {
const { navigate } = useNavigate(); const { navigate } = useNavigate();
useSetOnBack(() => { useSetOnBack(() => {
navigate(PATHS.additionalService.intro); navigate(PATHS.additionalService.intro);
}); });
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState('SMS');
const [formData, setFormData] = useState({
merchant: 'nictest001m',
productName: '',
productPrice: '',
orderNumber: '',
validDate: '2025.06.30'
});
const handlePaymentMethodChange = (method: string) => { const handlePaymentMethodChange = (method: LinkPaymentSendMethod) => {
setSelectedPaymentMethod(method); setFormData({ ...formData, sendMethod: method });
}; };
const handleInputChange = (field: string, value: string) => { const handleInputChange = (field: string, value: string) => {
setFormData(prev => ({ setFormData({ ...formData, [field]: value });
...prev, };
[field]: value
})); const handleDateChange = (date: string) => {
setFormData({ ...formData, paymentExpiryDate: date });
}; };
return ( return (
@@ -38,8 +36,8 @@ export const LinkPaymentStep1 = () => {
<div className="issue-field"> <div className="issue-field">
<select <select
className="wid-100" className="wid-100"
value={formData.merchant} value={formData.mid}
onChange={(e) => handleInputChange('merchant', e.target.value)} onChange={(e) => handleInputChange('mid', e.target.value)}
> >
<option>nictest001m</option> <option>nictest001m</option>
</select> </select>
@@ -51,20 +49,20 @@ export const LinkPaymentStep1 = () => {
<div className="issue-field"> <div className="issue-field">
<div className="chip-row"> <div className="chip-row">
<span <span
className={`keyword-tag flex-1 ${selectedPaymentMethod === 'SMS' ? 'active' : ''}`} className={`keyword-tag flex-1 ${formData.sendMethod === 'SMS' ? 'active' : ''}`}
onClick={() => handlePaymentMethodChange('SMS')} onClick={() => handlePaymentMethodChange(LinkPaymentSendMethod.SMS)}
> >
SMS SMS
</span> </span>
<span <span
className={`keyword-tag flex-1 ${selectedPaymentMethod === '이메일' ? 'active' : ''}`} className={`keyword-tag flex-1 ${formData.sendMethod === 'EMAIL' ? 'active' : ''}`}
onClick={() => handlePaymentMethodChange('이메일')} onClick={() => handlePaymentMethodChange(LinkPaymentSendMethod.EMAIL)}
> >
</span> </span>
<span <span
className={`keyword-tag flex-1 ${selectedPaymentMethod === '카카오' ? 'active' : ''}`} className={`keyword-tag flex-1 ${formData.sendMethod === 'KAKAO' ? 'active' : ''}`}
onClick={() => handlePaymentMethodChange('카카오')} onClick={() => handlePaymentMethodChange(LinkPaymentSendMethod.KAKAO)}
> >
</span> </span>
@@ -78,8 +76,8 @@ export const LinkPaymentStep1 = () => {
<input <input
type="text" type="text"
placeholder="" placeholder=""
value={formData.productName} value={formData.goodsName}
onChange={(e) => handleInputChange('productName', e.target.value)} onChange={(e) => handleInputChange('goodsName', e.target.value)}
/> />
</div> </div>
</div> </div>
@@ -90,8 +88,8 @@ export const LinkPaymentStep1 = () => {
<input <input
type="text" type="text"
placeholder="" placeholder=""
value={formData.productPrice} value={formData.amount}
onChange={(e) => handleInputChange('productPrice', e.target.value)} onChange={(e) => handleInputChange('amount', e.target.value)}
/> />
</div> </div>
</div> </div>
@@ -102,8 +100,8 @@ export const LinkPaymentStep1 = () => {
<input <input
type="text" type="text"
placeholder="" placeholder=""
value={formData.orderNumber} value={formData.moid}
onChange={(e) => handleInputChange('orderNumber', e.target.value)} onChange={(e) => handleInputChange('moid', e.target.value)}
/> />
</div> </div>
</div> </div>
@@ -112,20 +110,11 @@ export const LinkPaymentStep1 = () => {
<div className="issue-label"> </div> <div className="issue-label"> </div>
<div className="issue-field"> <div className="issue-field">
<div className="link-apply-date"> <div className="link-apply-date">
<div className="input-wrapper date"> <SingleDatePicker
<input date={formData.paymentExpiryDate}
type="text" setDate={handleDateChange}
value={formData.validDate} placeholder="날짜 선택"
className="date-input" />
onChange={(e) => handleInputChange('validDate', e.target.value)}
/>
<button type="button" className="date-btn">
<img
src={IMAGE_ROOT + '/ico_date.svg'}
alt="날짜 선택"
/>
</button>
</div>
<span></span> <span></span>
</div> </div>
</div> </div>

View File

@@ -1,45 +1,40 @@
import {ProcessStep} from "@/entities/transaction/model/types"; import {ProcessStep} from "@/entities/transaction/model/types";
import {useSetOnBack} from "@/widgets/sub-layout/use-sub-layout"; import {useSetOnBack} from "@/widgets/sub-layout/use-sub-layout";
import {useState} from "react"; import { LinkPaymentFormData, IdentityType, Language, LinkContentType } from '@/entities/additional-service/model/types'
export interface LinkPaymentStep2Props { export interface LinkPaymentStep2Props {
setProcessStep: ((processStep: ProcessStep) => void); setProcessStep: ((processStep: ProcessStep) => void);
formData: LinkPaymentFormData;
setFormData: (formData: LinkPaymentFormData) => void;
} }
export const LinkPaymentStep2 = ({ export const LinkPaymentStep2 = ({
setProcessStep setProcessStep,
formData,
setFormData
}: LinkPaymentStep2Props) => { }: LinkPaymentStep2Props) => {
useSetOnBack(() => { useSetOnBack(() => {
setProcessStep(ProcessStep.One); setProcessStep(ProcessStep.One);
}); });
const handleInputChange = (field: string, value: string) => { const handleInputChange = (field: string, value: string) => {
setFormData(prev => ({ setFormData({ ...formData, [field]: value });
...prev,
[field]: value
}));
}; };
const [selectedPurchaserType, setSelectedPurchaserType] = useState('개인'); const handleIdendityTypeChange = (type: IdentityType) => {
const [selectedLanguage, setSelectedLanguage] = useState('국문') setFormData({ ...formData, identityType: type });
const [selectedLinkContent, setSelectedLinkContent] = useState('기본');
const [formData, setFormData] = useState({
purchaser: '',
purchaserEmail: '',
purchaserPhone: '',
purchaserBirth: ''
});
const handlePurchaserTypeChange = (type: string) => {
setSelectedPurchaserType(type)
} }
const handleLanguageType = (type: string) => { const handleLanguageType = (type: Language) => {
setSelectedLanguage(type) setFormData({ ...formData, language: type });
} }
const handleLinkContent = (content: string) => { const handleLinkContent = (content: LinkContentType) => {
setSelectedLinkContent(content) setFormData({ ...formData, linkContentType: content });
}
const handleIdentityToggle = (isChecked: boolean) => {
setFormData({ ...formData, isIdentity: isChecked });
} }
return ( return (
@@ -51,8 +46,8 @@ export const LinkPaymentStep2 = ({
<input <input
type="text" type="text"
placeholder="" placeholder=""
value={formData.purchaser} value={formData.buyerName}
onChange={(e) => handleInputChange('purchaser', e.target.value)} onChange={(e) => handleInputChange('buyerName', e.target.value)}
/> />
</div> </div>
</div> </div>
@@ -63,8 +58,8 @@ export const LinkPaymentStep2 = ({
<input <input
type="text" type="text"
placeholder="test@nicepay.co.kr" placeholder="test@nicepay.co.kr"
value={formData.purchaserEmail} value={formData.email}
onChange={(e) => handleInputChange('purchaserEmail', e.target.value)} onChange={(e) => handleInputChange('email', e.target.value)}
/> />
</div> </div>
</div> </div>
@@ -75,8 +70,8 @@ export const LinkPaymentStep2 = ({
<input <input
type="text" type="text"
placeholder="01012345678" placeholder="01012345678"
value={formData.purchaserPhone} value={formData.phoneNumber}
onChange={(e) => handleInputChange('purchaserPhone', e.target.value)} onChange={(e) => handleInputChange('phoneNumber', e.target.value)}
/> />
</div> </div>
</div> </div>
@@ -84,7 +79,7 @@ export const LinkPaymentStep2 = ({
<div className="issue-row gap-10 beetween"> <div className="issue-row gap-10 beetween">
<div className="issue-label wid-105"> </div> <div className="issue-label wid-105"> </div>
<label className="settings-switch"> <label className="settings-switch">
<input type="checkbox"/> <input type="checkbox" checked={formData.isIdentity} onChange={(e) => handleIdentityToggle(e.target.checked)}/>
<span className="slider"></span> <span className="slider"></span>
</label> </label>
</div> </div>
@@ -93,14 +88,14 @@ export const LinkPaymentStep2 = ({
<div className="issue-field"> <div className="issue-field">
<div className="chip-row"> <div className="chip-row">
<span <span
className={`keyword-tag flex-1 ${selectedPurchaserType === '개인' ? 'active' : ''}`} className={`keyword-tag flex-1 ${formData.identityType === IdentityType.INDIVIDUAL ? 'active' : ''}`}
onClick={() => handlePurchaserTypeChange('개인')} onClick={() => handleIdendityTypeChange(IdentityType.INDIVIDUAL)}
> >
</span> </span>
<span <span
className={`keyword-tag flex-1 ${selectedPurchaserType === '법인' ? 'active' : ''}`} className={`keyword-tag flex-1 ${formData.identityType === IdentityType.CORPORATE ? 'active' : ''}`}
onClick={() => handlePurchaserTypeChange('법인')} onClick={() => handleIdendityTypeChange(IdentityType.CORPORATE)}
> >
</span> </span>
@@ -112,10 +107,10 @@ export const LinkPaymentStep2 = ({
<div className="issue-label wid-105"></div> <div className="issue-label wid-105"></div>
<div className='issue-field'> <div className='issue-field'>
<input <input
type="text" type="number"
placeholder="생년월일 6자리" placeholder="생년월일 6자리"
value={formData.purchaserBirth} value={formData.identityValue}
onChange={(e) => handleInputChange('purchaserPhone', e.target.value)} onChange={(e) => handleInputChange('identityValue', e.target.value)}
/> />
</div> </div>
</div> </div>
@@ -125,14 +120,14 @@ export const LinkPaymentStep2 = ({
<div className="issue-field"> <div className="issue-field">
<div className="chip-row"> <div className="chip-row">
<span <span
className={`keyword-tag flex-1 ${selectedLanguage === '국문' ? 'active' : ''}`} className={`keyword-tag flex-1 ${formData.language === Language.KR ? 'active' : ''}`}
onClick={() => handleLanguageType('국문')} onClick={() => handleLanguageType(Language.KR)}
> >
</span> </span>
<span <span
className={`keyword-tag flex-1 ${selectedLanguage === '영문' ? 'active' : ''}`} className={`keyword-tag flex-1 ${formData.language === Language.EN ? 'active' : ''}`}
onClick={() => handleLanguageType('영문')} onClick={() => handleLanguageType(Language.EN)}
> >
</span> </span>
@@ -144,14 +139,14 @@ export const LinkPaymentStep2 = ({
<div className="issue-field"> <div className="issue-field">
<div className="chip-row"> <div className="chip-row">
<span <span
className={`keyword-tag flex-1 ${selectedLinkContent === '기본' ? 'active' : ''}`} className={`keyword-tag flex-1 ${formData.linkContentType === LinkContentType.BASIC ? 'active' : ''}`}
onClick={() => handleLinkContent('기본')} onClick={() => handleLinkContent(LinkContentType.BASIC)}
> >
</span> </span>
<span <span
className={`keyword-tag flex-1 ${selectedLinkContent === '추가' ? 'active' : ''}`} className={`keyword-tag flex-1 ${formData.linkContentType === LinkContentType.ADDITIONAL ? 'active' : ''}`}
onClick={() => handleLinkContent('추가')} onClick={() => handleLinkContent(LinkContentType.ADDITIONAL)}
> >
</span> </span>

View File

@@ -1,18 +1,58 @@
import { HeaderType } from '@/entities/common/model/types'; import { HeaderType } from '@/entities/common/model/types';
import { useSetFooterMode, useSetHeaderTitle, useSetHeaderType } from '@/widgets/sub-layout/use-sub-layout'; import { useSetFooterMode, useSetHeaderTitle, useSetHeaderType } from '@/widgets/sub-layout/use-sub-layout';
import { useNavigate } from '@/shared/lib/hooks/use-navigate'; import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { useLocation } from 'react-router';
import { IMAGE_ROOT } from "@/shared/constants/common"; import { IMAGE_ROOT } from "@/shared/constants/common";
import { PATHS } from '@/shared/constants/paths'; import { PATHS } from '@/shared/constants/paths';
import { LinkPaymentFormData } from '@/entities/additional-service/model/types'
import { useExtensionLinkPayRequestMutation } from '@/entities/additional-service/api/link-payment/use-extension-link-pay-request-mutation';
import { ExtensionLinkPayRequestParams } from '@/entities/additional-service/model/types';
export const LinkPaymentApplyConfirmPage = () => { export const LinkPaymentApplyConfirmPage = () => {
const { navigate } = useNavigate(); const { navigate } = useNavigate();
const location = useLocation();
const formData: LinkPaymentFormData = location.state?.formData;
const { mutateAsync: linkPayRequest } = useExtensionLinkPayRequestMutation();
useSetHeaderTitle('메시지 미리보기'); useSetHeaderTitle('메시지 미리보기');
useSetHeaderType(HeaderType.LeftArrow); useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false); useSetFooterMode(false);
const onClickToConfirm = () => { const onClickToConfirm = () => {
navigate(PATHS.additionalService.linkPayment.confirmSuccess); if (!formData) {
console.error('Form data is missing');
return;
}
const requestParams: ExtensionLinkPayRequestParams = {
mid: formData.mid,
sendMethod: formData.sendMethod,
goodsName: formData.goodsName,
amount: formData.amount,
moid: formData.moid,
paymentExpiryDate: formData.paymentExpiryDate.replace(/\./g, ''),
buyerName: formData.buyerName,
email: formData.email,
phoneNumber: formData.phoneNumber,
isIdentity: formData.isIdentity,
identityType: formData.identityType,
identityValue: formData.identityValue,
language: formData.language,
linkContentType: formData.linkContentType
};
console.log("Link Payment 요청 파라미터: ", requestParams);
linkPayRequest(requestParams)
.then((response) => {
console.log("Link Payment 성공 응답: ", response);
navigate(PATHS.additionalService.linkPayment.confirmSuccess);
})
.catch((error) => {
console.error("Link Payment 실패: ", error);
// 에러 처리 로직 추가 가능
});
}; };
const onClickToBack = () => { const onClickToBack = () => {
@@ -37,8 +77,8 @@ export const LinkPaymentApplyConfirmPage = () => {
!$&#123;pay_url&#125;<br/><br/> !$&#123;pay_url&#125;<br/><br/>
<b> <b>
상호 : 나이스페이먼츠 <br/> 상호 : 나이스페이먼츠 <br/>
상품명 : TEST<br/> : {formData.goodsName}<br/>
금액 : 123,123 : {formData.amount}
</b> </b>
</p> </p>
</div> </div>

View File

@@ -6,22 +6,47 @@ import {ProcessStep} from '@/entities/transaction/model/types';
import {useSetFooterMode, useSetHeaderTitle, useSetHeaderType} from '@/widgets/sub-layout/use-sub-layout'; import {useSetFooterMode, useSetHeaderTitle, useSetHeaderType} from '@/widgets/sub-layout/use-sub-layout';
import {useNavigate} from '@/shared/lib/hooks/use-navigate'; import {useNavigate} from '@/shared/lib/hooks/use-navigate';
import {PATHS} from "@/shared/constants/paths"; import {PATHS} from "@/shared/constants/paths";
import { LinkPaymentFormData, IdentityType, Language, LinkContentType, LinkPaymentSendMethod } from '@/entities/additional-service/model/types';
export const LinkPaymentApplyPage = () => { export const LinkPaymentApplyPage = () => {
const { navigate } = useNavigate(); const { navigate } = useNavigate();
const [processStep, setProcessStep] = useState<ProcessStep>(ProcessStep.One); const [processStep, setProcessStep] = useState<ProcessStep>(ProcessStep.One);
const [formData, setFormData] = useState<LinkPaymentFormData>({
mid: 'nictest001m',
sendMethod: LinkPaymentSendMethod.SMS,
goodsName: '',
amount: 0,
moid: '',
paymentExpiryDate: '',
buyerName: '',
email: '',
phoneNumber: '',
isIdentity: false,
identityType: IdentityType.INDIVIDUAL,
identityValue: '',
language: Language.KR,
linkContentType: LinkContentType.BASIC
});
useSetHeaderTitle('링크결제 신청'); useSetHeaderTitle('링크결제 신청');
useSetHeaderType(HeaderType.LeftArrow); useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false); useSetFooterMode(false);
const onClickToBack = () => {
navigate(-1);
};
const onClickToChangeTab = () => { const onClickToChangeTab = () => {
if(processStep === ProcessStep.One) { if(processStep === ProcessStep.One) {
setProcessStep(ProcessStep.Two); setProcessStep(ProcessStep.Two);
} }
else if(processStep === ProcessStep.Two) { else if(processStep === ProcessStep.Two) {
navigate(PATHS.additionalService.linkPayment.requestConfirm) navigate(PATHS.additionalService.linkPayment.requestConfirm, {
state: { formData }
});
} }
}; };
@@ -49,11 +74,16 @@ export const LinkPaymentApplyPage = () => {
</div> </div>
{(processStep === ProcessStep.One) && {(processStep === ProcessStep.One) &&
<LinkPaymentStep1 ></LinkPaymentStep1> <LinkPaymentStep1
formData={formData}
setFormData={setFormData}
></LinkPaymentStep1>
} }
{ (processStep === ProcessStep.Two) && { (processStep === ProcessStep.Two) &&
<LinkPaymentStep2 <LinkPaymentStep2
setProcessStep={ setProcessStep } setProcessStep={ setProcessStep }
formData={formData}
setFormData={setFormData}
></LinkPaymentStep2> ></LinkPaymentStep2>
} }
@@ -69,8 +99,9 @@ export const LinkPaymentApplyPage = () => {
{(processStep === ProcessStep.Two) && {(processStep === ProcessStep.Two) &&
<div className="apply-row two-button"> <div className="apply-row two-button">
<button <button
className="btn-50 btn-darkgray flex-1"> className="btn-50 btn-darkgray flex-1"
</button> onClick={() => onClickToBack() }
></button>
<button <button
className="btn-50 btn-blue flex-3" className="btn-50 btn-blue flex-3"
onClick={() => onClickToChangeTab() } onClick={() => onClickToChangeTab() }

View File

@@ -25,6 +25,10 @@ export const API_URL_ADDITIONAL_SERVICE = {
// POST: 링크결제 - 발송내역 엑셀 다운 // POST: 링크결제 - 발송내역 엑셀 다운
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/linkpay/history/excel`; return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/linkpay/history/excel`;
}, },
extensionLinkPaymentRequest: () => {
// POST: 링크결제 - 결제 신청
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/linkpay/request`;
},
extensionLinkPaymentHistoryDetail: () => { extensionLinkPaymentHistoryDetail: () => {
// POST: 링크결제 - 발송내역 상세 조회 // POST: 링크결제 - 발송내역 상세 조회
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/linkpay/history/detail`; return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/linkpay/history/detail`;
@@ -45,7 +49,7 @@ export const API_URL_ADDITIONAL_SERVICE = {
// POST: 링크결제 - 발송대기 상세 내역 조회 // POST: 링크결제 - 발송대기 상세 내역 조회
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/linkpay/wait/detail`; return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/linkpay/wait/detail`;
}, },
extensionLinkPayMentWaitDelete: () => { extensionLinkPaymentWaitDelete: () => {
// POST: 링크결제 - 발송대기 삭제 // POST: 링크결제 - 발송대기 삭제
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/linkpay/wait/delete`; return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/linkpay/wait/delete`;
}, },

View File

@@ -0,0 +1,90 @@
import { useState } from 'react';
import { CalendarType } from '@/entities/common/model/types';
import { IMAGE_ROOT } from '@/shared/constants/common';
import NiceCalendar from '../calendar/nice-calendar';
interface SingleDatePickerProps {
title?: string;
date: string;
setDate: (date: string) => void;
placeholder?: string;
}
export const SingleDatePicker = ({
title,
date,
setDate,
placeholder = '날짜 선택'
}: SingleDatePickerProps) => {
const [calendarOpen, setCalendarOpen] = useState<boolean>(false);
const onClickToOpenCalendar = () => {
setCalendarOpen(true);
};
const setNewDate = (newDate: string) => {
setDate(newDate);
setCalendarOpen(false);
};
return (
<>
{title && (
<div className="issue-row gap-10">
<div className="issue-label">{title}</div>
<div className="issue-field">
<div className="input-wrapper date">
<input
type="text"
value={date}
className="date-input"
placeholder={placeholder}
readOnly
/>
<button
type="button"
className="date-btn"
onClick={onClickToOpenCalendar}
>
<img
src={IMAGE_ROOT + '/ico_date.svg'}
alt="날짜 선택"
/>
</button>
</div>
</div>
</div>
)}
{!title && (
<div className="input-wrapper date">
<input
type="text"
value={date}
className="date-input"
placeholder={placeholder}
/>
<button
type="button"
className="date-btn"
onClick={onClickToOpenCalendar}
>
<img
src={IMAGE_ROOT + '/ico_date.svg'}
alt="날짜 선택"
/>
</button>
</div>
)}
<NiceCalendar
calendarOpen={calendarOpen}
setCalendarOpen={setCalendarOpen}
singleDate={date}
calendarType={CalendarType.Single}
setNewDate={setNewDate}
></NiceCalendar>
</>
);
};