test
This commit is contained in:
@@ -33,8 +33,10 @@ export interface QnaItem {
|
||||
cursorId?: number;
|
||||
seq?: string;
|
||||
statusCode?: string;
|
||||
requestType?: string;
|
||||
requestDate?: string;
|
||||
requestName?: string;
|
||||
answerDate?: string;
|
||||
title?: string;
|
||||
contents?: string;
|
||||
answer?: string;
|
||||
@@ -46,7 +48,7 @@ export interface QnaItemProps extends QnaItem {
|
||||
|
||||
};
|
||||
export interface QnaSaveParams extends SupportParams {
|
||||
counselType: string;
|
||||
requestType: string;
|
||||
requestName: string;
|
||||
requestTel: string;
|
||||
requestEmail: string;
|
||||
|
||||
@@ -8,8 +8,10 @@ export const SupportQnaItem = ({
|
||||
cursorId,
|
||||
seq,
|
||||
statusCode,
|
||||
requestType,
|
||||
requestDate,
|
||||
requestName,
|
||||
answerDate,
|
||||
title,
|
||||
contents,
|
||||
answer
|
||||
@@ -20,8 +22,10 @@ export const SupportQnaItem = ({
|
||||
navigate(PATHS.support.qna.detail, {
|
||||
state: {
|
||||
statusCode,
|
||||
requestType,
|
||||
requestDate,
|
||||
requestName,
|
||||
answerDate,
|
||||
title,
|
||||
contents,
|
||||
answer
|
||||
|
||||
@@ -127,6 +127,14 @@
|
||||
"01": "Save",
|
||||
"02": "Submit Inquiry",
|
||||
"03": "Completed"
|
||||
},
|
||||
"validation": {
|
||||
"title": "Title is Required.",
|
||||
"requestType": "RequestType is Required.",
|
||||
"requestName": "RequestName is Required.",
|
||||
"requestTel": "Phone number is Required.",
|
||||
"requestEmail": "Invalid Email Type.",
|
||||
"contents": "Contents is Required."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,14 @@
|
||||
"01": "저장",
|
||||
"02": "문의",
|
||||
"03": "답변완료"
|
||||
},
|
||||
"validation": {
|
||||
"title": "제목은 필수 항목 입니다.",
|
||||
"requestType": "문의유형은 필수 항목 입니다.",
|
||||
"requestName": "요청자명은 필수 항목 입니다.",
|
||||
"requestTel": "휴대폰번호은 필수 항목 입니다.",
|
||||
"requestEmail": "이메일 형식이 맞지 않습니다.",
|
||||
"contents": "문의내용은 필수 항목 입니다."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,23 @@ import {
|
||||
useSetFooterMode,
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const QnaDetailPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
|
||||
const [statusCode, setStatusCode] = useState<string>('');
|
||||
const [requestDate, setRequestDate] = useState<string>('');
|
||||
const [requestName, setRequestName] = useState<string>('');
|
||||
const [requestType, setRequestType] = useState<string>('');
|
||||
const [answerDate, setAnswerDate] = useState<string>('');
|
||||
const [title, setTitle] = useState<string>('');
|
||||
const [contents, setContents] = useState<string>('');
|
||||
const [answer, setAnswer] = useState<string>('');
|
||||
|
||||
useSetHeaderTitle('1:1 문의');
|
||||
useSetHeaderTitle(t('support.qna.title'));
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(false);
|
||||
useSetOnBack(() => {
|
||||
@@ -31,8 +35,10 @@ export const QnaDetailPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setStatusCode(location?.state.statusCode);
|
||||
setRequestType(location?.state.requestType);
|
||||
setRequestDate(location?.state.requestDate);
|
||||
setRequestName(location?.state.requestName);
|
||||
setAnswerDate(location?.state.answerDate);
|
||||
setTitle(location?.state.title);
|
||||
setContents(location?.state.contents);
|
||||
setAnswer(location?.state.answer);
|
||||
@@ -47,11 +53,11 @@ export const QnaDetailPage = () => {
|
||||
<div className="inq-detail__head">
|
||||
<div className="inq-detail__row">
|
||||
<span className="inq-badge">제목</span>
|
||||
<span className="inq-head-text bold">{ requestName }</span>
|
||||
<span className="inq-head-text bold">{ title }</span>
|
||||
</div>
|
||||
<div className="inq-detail__row">
|
||||
<span className="inq-badge">유형</span>
|
||||
<span className="inq-head-text">한도/보증보험 문의</span>
|
||||
<span className="inq-head-text">{ t(`support.qna.categories.${requestType}`) }</span>
|
||||
</div>
|
||||
<div className="inq-detail__row">
|
||||
<span className="inq-badge">등록일</span>
|
||||
@@ -59,7 +65,7 @@ export const QnaDetailPage = () => {
|
||||
</div>
|
||||
<div className="inq-detail__row">
|
||||
<span className="inq-badge">답변일</span>
|
||||
<span className="inq-head-text">2025.07.31</span>
|
||||
<span className="inq-head-text">{ moment(answerDate).format('YYYY.MM.DD') }</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="inq-detail__divider"></div>
|
||||
@@ -70,18 +76,6 @@ export const QnaDetailPage = () => {
|
||||
<div className="inq-detail__section">
|
||||
<div className="inq-detail__section-title">문의 내용</div>
|
||||
<div className="inq-detail__body" dangerouslySetInnerHTML={{ __html: contents || '' }}></div>
|
||||
{/*
|
||||
<div className="inq-detail__box">
|
||||
<div className="inq-detail__kv">
|
||||
<div className="k">제목</div>
|
||||
<div className="v">{ title }</div>
|
||||
</div>
|
||||
<div className="inq-detail__kv">
|
||||
<div className="k">내용</div>
|
||||
<div className="v" dangerouslySetInnerHTML={{ __html: contents || '' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { HeaderType } from '@/entities/common/model/types';
|
||||
import { DefaultRequestPagination, HeaderType } from '@/entities/common/model/types';
|
||||
import { useQnaListMutation } from '@/entities/support/api/use-qna-list-mutation';
|
||||
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constant';
|
||||
import { QnaItem, QnaListParams, QnaListResponse } from '@/entities/support/model/types';
|
||||
@@ -14,18 +14,45 @@ import {
|
||||
useSetOnBack
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
import { useStore } from '@/shared/model/store';
|
||||
import useIntersectionObserver from '@/widgets/intersection-observer';
|
||||
|
||||
export const QnaListPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const [onActionIntersect, setOnActionIntersect] = useState<boolean>(false);
|
||||
|
||||
const onIntersect: IntersectionObserverCallback = (entries: Array<IntersectionObserverEntry>) => {
|
||||
|
||||
entries.forEach((entry: IntersectionObserverEntry) => {
|
||||
if(onActionIntersect){
|
||||
if(entry.isIntersecting){
|
||||
console.log('Element is now intersecting with the root.');
|
||||
callList();
|
||||
}
|
||||
else{
|
||||
console.log('Element is no longer intersecting with the root.');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
const { setTarget } = useIntersectionObserver({
|
||||
threshold: 1,
|
||||
onIntersect
|
||||
});
|
||||
|
||||
|
||||
const midOptions = useStore.getState().UserStore.selectOptionsMids;
|
||||
const userMid = useStore.getState().UserStore.mid;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [mid, setMid] = useState<string>(userMid);
|
||||
const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM);
|
||||
const [pageParam, setPageParam] = useState<DefaultRequestPagination>(DEFAULT_PAGE_PARAM);
|
||||
const [nextCursor, setNextCursor] = useState<string | null>(null);
|
||||
const [statusCode, setStatusCode] = useState<string>(''); // 02, 03
|
||||
const [resultList, setResultList] = useState<Array<QnaItem>>([]);
|
||||
|
||||
|
||||
useSetHeaderTitle(t('support.qna.title'));
|
||||
useSetHeaderType(HeaderType.LeftArrow);
|
||||
useSetFooterMode(false);
|
||||
@@ -35,17 +62,36 @@ export const QnaListPage = () => {
|
||||
|
||||
const { mutateAsync: qnaList } = useQnaListMutation();
|
||||
const callList = () => {
|
||||
setOnActionIntersect(false);
|
||||
let listParams: QnaListParams = {
|
||||
mid: mid,
|
||||
statusCode: statusCode,
|
||||
...{page: pageParam}
|
||||
...{
|
||||
page: pageParam
|
||||
}
|
||||
};
|
||||
|
||||
qnaList(listParams).then((rs: QnaListResponse) => {
|
||||
setResultList(rs.content);
|
||||
setResultList([
|
||||
...resultList,
|
||||
...rs.content
|
||||
]);
|
||||
if(rs.hasNext){
|
||||
setNextCursor(rs.nextCursor);
|
||||
setPageParam({
|
||||
...pageParam,
|
||||
cursor: rs.nextCursor
|
||||
});
|
||||
setOnActionIntersect(true);
|
||||
}
|
||||
else{
|
||||
setNextCursor(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
const getQnaList = () => {
|
||||
let rs = [];
|
||||
for(let i=0;i<resultList.length;i++){
|
||||
@@ -55,8 +101,10 @@ export const QnaListPage = () => {
|
||||
cursorId={ resultList[i]?.cursorId }
|
||||
seq={ resultList[i]?.seq }
|
||||
statusCode={ resultList[i]?.statusCode }
|
||||
requestType={ resultList[i]?.requestType }
|
||||
requestDate={ resultList[i]?.requestDate }
|
||||
requestName={ resultList[i]?.requestName }
|
||||
answerDate={ resultList[i]?.answerDate }
|
||||
title={ resultList[i]?.title }
|
||||
contents={ resultList[i]?.contents }
|
||||
answer={ resultList[i]?.answer }
|
||||
@@ -74,6 +122,10 @@ export const QnaListPage = () => {
|
||||
callList();
|
||||
}, [statusCode]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [resultList]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
@@ -93,7 +145,6 @@ export const QnaListPage = () => {
|
||||
<option
|
||||
key={ value.value }
|
||||
value={ value.value }
|
||||
selected={ (userMid === value.value)? true: false }
|
||||
>{ value.name }</option>
|
||||
))
|
||||
}
|
||||
@@ -111,8 +162,9 @@ export const QnaListPage = () => {
|
||||
<option value="03">{t('support.qna.statusCode.03')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="inq-list">
|
||||
{ getQnaList() }
|
||||
<div className="inq-list" >
|
||||
{ getQnaList() }
|
||||
<div ref={setTarget}></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row">
|
||||
@@ -124,6 +176,7 @@ export const QnaListPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -12,10 +12,13 @@ import {
|
||||
import { useStore } from '@/shared/model/store';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PatternFormat } from 'react-number-format';
|
||||
import { overlay } from 'overlay-kit';
|
||||
import { Dialog } from '@/shared/ui/dialogs/dialog';
|
||||
import { QnaSaveParams, QnaSaveResponse } from '@/entities/support/model/types';
|
||||
|
||||
export enum QnaRegisterPropsName {
|
||||
Mid = 'Mid',
|
||||
CounselType = 'CounselType',
|
||||
RequestType = 'RequestType',
|
||||
RequestName = 'RequestName',
|
||||
RequestTel = 'RequestTel',
|
||||
RequestEmail = 'RequestEmail',
|
||||
@@ -29,7 +32,7 @@ export const QnaRegisterPage = () => {
|
||||
const userMid = useStore.getState().UserStore.mid;
|
||||
|
||||
const [mid, setMid] = useState<string>(userMid);
|
||||
const [counselType, setCounselType] = useState<string>('st');
|
||||
const [requestType, setRequestType] = useState<string>('');
|
||||
const [requestName, setRequestName] = useState<string>('');
|
||||
const [requestTel, setRequestTel] = useState<string>('');
|
||||
const [requestEmail, setRequestEmail] = useState<string>('');
|
||||
@@ -53,8 +56,8 @@ export const QnaRegisterPage = () => {
|
||||
if(key === QnaRegisterPropsName.Mid){
|
||||
setMid(value);
|
||||
}
|
||||
else if(key === QnaRegisterPropsName.CounselType){
|
||||
setCounselType(value);
|
||||
else if(key === QnaRegisterPropsName.RequestType){
|
||||
setRequestType(value);
|
||||
}
|
||||
else if(key === QnaRegisterPropsName.RequestName){
|
||||
setRequestName(value);
|
||||
@@ -72,18 +75,67 @@ export const QnaRegisterPage = () => {
|
||||
setContents(value);
|
||||
}
|
||||
};
|
||||
|
||||
const showAlert = (msg: string) => {
|
||||
overlay.open(({
|
||||
isOpen,
|
||||
close,
|
||||
unmount
|
||||
}) => {
|
||||
return (
|
||||
<Dialog
|
||||
afterLeave={unmount}
|
||||
open={isOpen}
|
||||
onClose={close}
|
||||
message={ msg }
|
||||
buttonLabel={['확인']}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
const checkEmail = (email: string) => {
|
||||
var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
|
||||
if(email != '' && email != 'undefined' && re.test(email)){
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const callRegister = () => {
|
||||
let params = {
|
||||
if(!title){
|
||||
showAlert(t('support.qna.validation.title'));
|
||||
return;
|
||||
}
|
||||
else if(!requestType){
|
||||
showAlert(t('support.qna.validation.requestType'));
|
||||
return;
|
||||
}
|
||||
else if(!requestName){
|
||||
showAlert(t('support.qna.validation.requestName'));
|
||||
return;
|
||||
}
|
||||
else if(!requestTel){
|
||||
showAlert(t('support.qna.validation.requestTel'));
|
||||
return;
|
||||
}
|
||||
else if(requestEmail && !checkEmail(requestEmail)){
|
||||
showAlert(t('support.qna.validation.requestEmail'));
|
||||
return;
|
||||
}
|
||||
else if(!contents){
|
||||
showAlert(t('support.qna.validation.contents'));
|
||||
return;
|
||||
}
|
||||
let params: QnaSaveParams = {
|
||||
mid: mid,
|
||||
title: title,
|
||||
counselType: counselType,
|
||||
requestType: requestType,
|
||||
requestName: requestName,
|
||||
requestTel: requestTel,
|
||||
requestEmail: requestEmail,
|
||||
contents: contents,
|
||||
};
|
||||
qnaSave(params).then((rs) => {
|
||||
qnaSave(params).then((rs: QnaSaveResponse) => {
|
||||
alert('성공');
|
||||
navigate(PATHS.support.qna.list);
|
||||
});
|
||||
@@ -118,9 +170,9 @@ export const QnaRegisterPage = () => {
|
||||
</div>
|
||||
<div className="inq-control">
|
||||
<select
|
||||
value={ counselType }
|
||||
value={ requestType }
|
||||
required= { true }
|
||||
onChange={ (e: ChangeEvent<HTMLSelectElement>) => setInputValue(e, QnaRegisterPropsName.CounselType) }
|
||||
onChange={ (e: ChangeEvent<HTMLSelectElement>) => setInputValue(e, QnaRegisterPropsName.RequestType) }
|
||||
>
|
||||
<option value="">{t('support.qna.categories.choose')}</option>
|
||||
<option value="01">{t('support.qna.categories.01')}</option>
|
||||
@@ -164,7 +216,7 @@ export const QnaRegisterPage = () => {
|
||||
<div className="inq-label">이메일 주소</div>
|
||||
<div className="inq-control">
|
||||
<input
|
||||
type="text"
|
||||
type="email"
|
||||
placeholder="TEST123@nicepay.com"
|
||||
value={ requestEmail }
|
||||
onChange={ (e: ChangeEvent<HTMLInputElement>) => setInputValue(e, QnaRegisterPropsName.RequestEmail) }
|
||||
|
||||
33
src/widgets/intersection-observer/index.ts
Normal file
33
src/widgets/intersection-observer/index.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface useIntersectionObserverProps {
|
||||
root?: null;
|
||||
rootMargin?: string;
|
||||
threshold?: number;
|
||||
onIntersect: IntersectionObserverCallback;
|
||||
}
|
||||
|
||||
const useIntersectionObserver = ({
|
||||
root,
|
||||
rootMargin = '0px',
|
||||
threshold = 0,
|
||||
onIntersect
|
||||
}: useIntersectionObserverProps) => {
|
||||
const [target, setTarget] = useState<HTMLElement | null | undefined>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!target) return;
|
||||
|
||||
const observer: IntersectionObserver = new IntersectionObserver(
|
||||
onIntersect,
|
||||
{ root, rootMargin, threshold }
|
||||
);
|
||||
observer.observe(target);
|
||||
|
||||
return () => observer.unobserve(target);
|
||||
}, [onIntersect, root, rootMargin, target, threshold]);
|
||||
|
||||
return { setTarget };
|
||||
};
|
||||
|
||||
export default useIntersectionObserver;
|
||||
Reference in New Issue
Block a user