From dc6d80eb81a0004972b6827b9a78d1af90098e86 Mon Sep 17 00:00:00 2001 From: "focp212@naver.com" Date: Fri, 17 Oct 2025 17:23:32 +0900 Subject: [PATCH] test --- src/entities/support/model/types.ts | 4 +- src/entities/support/ui/qna-item.tsx | 4 ++ src/locales/en.json | 8 +++ src/locales/ko.json | 8 +++ src/pages/support/qna/detail-page.tsx | 26 +++----- src/pages/support/qna/list-page.tsx | 69 +++++++++++++++++--- src/pages/support/qna/register-page.tsx | 74 ++++++++++++++++++---- src/widgets/intersection-observer/index.ts | 33 ++++++++++ 8 files changed, 190 insertions(+), 36 deletions(-) create mode 100644 src/widgets/intersection-observer/index.ts diff --git a/src/entities/support/model/types.ts b/src/entities/support/model/types.ts index 7c68cc0..cacaff7 100644 --- a/src/entities/support/model/types.ts +++ b/src/entities/support/model/types.ts @@ -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; diff --git a/src/entities/support/ui/qna-item.tsx b/src/entities/support/ui/qna-item.tsx index b56e353..62b6c77 100644 --- a/src/entities/support/ui/qna-item.tsx +++ b/src/entities/support/ui/qna-item.tsx @@ -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 diff --git a/src/locales/en.json b/src/locales/en.json index f2c9963..8b9c630 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -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." } } } diff --git a/src/locales/ko.json b/src/locales/ko.json index 36ecca8..7911a1e 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -127,6 +127,14 @@ "01": "저장", "02": "문의", "03": "답변완료" + }, + "validation": { + "title": "제목은 필수 항목 입니다.", + "requestType": "문의유형은 필수 항목 입니다.", + "requestName": "요청자명은 필수 항목 입니다.", + "requestTel": "휴대폰번호은 필수 항목 입니다.", + "requestEmail": "이메일 형식이 맞지 않습니다.", + "contents": "문의내용은 필수 항목 입니다." } } } diff --git a/src/pages/support/qna/detail-page.tsx b/src/pages/support/qna/detail-page.tsx index 70ffc59..e159038 100644 --- a/src/pages/support/qna/detail-page.tsx +++ b/src/pages/support/qna/detail-page.tsx @@ -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(''); const [requestDate, setRequestDate] = useState(''); const [requestName, setRequestName] = useState(''); + const [requestType, setRequestType] = useState(''); + const [answerDate, setAnswerDate] = useState(''); const [title, setTitle] = useState(''); const [contents, setContents] = useState(''); const [answer, setAnswer] = useState(''); - 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 = () => {
제목 - { requestName } + { title }
유형 - 한도/보증보험 문의 + { t(`support.qna.categories.${requestType}`) }
등록일 @@ -59,7 +65,7 @@ export const QnaDetailPage = () => {
답변일 - 2025.07.31 + { moment(answerDate).format('YYYY.MM.DD') }
@@ -70,18 +76,6 @@ export const QnaDetailPage = () => {
문의 내용
- {/* -
-
-
제목
-
{ title }
-
-
-
내용
-
-
-
- */}
diff --git a/src/pages/support/qna/list-page.tsx b/src/pages/support/qna/list-page.tsx index 89fdf4c..4339b37 100644 --- a/src/pages/support/qna/list-page.tsx +++ b/src/pages/support/qna/list-page.tsx @@ -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(false); + + const onIntersect: IntersectionObserverCallback = (entries: Array) => { + + 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(userMid); - const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM); + const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM); + const [nextCursor, setNextCursor] = useState(null); const [statusCode, setStatusCode] = useState(''); // 02, 03 const [resultList, setResultList] = useState>([]); + 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 { 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 ( <>
@@ -93,7 +145,6 @@ export const QnaListPage = () => { )) } @@ -111,8 +162,9 @@ export const QnaListPage = () => { -
- { getQnaList() } +
+ { getQnaList() } +
@@ -124,6 +176,7 @@ export const QnaListPage = () => {
+ ); }; \ No newline at end of file diff --git a/src/pages/support/qna/register-page.tsx b/src/pages/support/qna/register-page.tsx index aa9dab3..04dbe47 100644 --- a/src/pages/support/qna/register-page.tsx +++ b/src/pages/support/qna/register-page.tsx @@ -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(userMid); - const [counselType, setCounselType] = useState('st'); + const [requestType, setRequestType] = useState(''); const [requestName, setRequestName] = useState(''); const [requestTel, setRequestTel] = useState(''); const [requestEmail, setRequestEmail] = useState(''); @@ -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 ( + + ); + }); + } + 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 = () => {
) => setInputValue(e, QnaRegisterPropsName.RequestEmail) } diff --git a/src/widgets/intersection-observer/index.ts b/src/widgets/intersection-observer/index.ts new file mode 100644 index 0000000..54a9c6d --- /dev/null +++ b/src/widgets/intersection-observer/index.ts @@ -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(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; \ No newline at end of file