This commit is contained in:
focp212@naver.com
2025-09-08 17:14:21 +09:00
parent c92fdc2bd1
commit 065a4fd348
15 changed files with 319 additions and 91 deletions

View File

@@ -10,7 +10,7 @@ import {
export const codesCacheRefresh = () => {
return resultify(
axios.post<CodesCacheRefreshResponse>(API_URL.counselList()),
axios.post<CodesCacheRefreshResponse>(API_URL.codesCacheRefresh()),
);
};

View File

@@ -1,8 +1,8 @@
export const DEFAULT_PAGE_PARAM = {
cursor: 'string',
page: 0,
size: 0,
sortBy: 'string',
sortOrder: 'ASC',
orderBy: 'string',
limit: 0,
offset: 0
};

View File

@@ -1,3 +1,11 @@
export interface DefaultRequestPagination {
page: number;
size: number;
sortBy: string;
sortOrder: string;
orderBy: string;
offset: number;
};
export interface DefaulResponsePagination {
nextCursor: string | null;
hasNext: boolean;

View File

@@ -1,29 +0,0 @@
import axios from 'axios';
import { API_URL } from '@/shared/api/urls';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import {
CounselListParams,
CounselListResponse
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const counselList = (params: CounselListParams) => {
return resultify(
axios.post<CounselListResponse>(API_URL.counselList(), params),
);
};
export const useCounselListMutation = (options?: UseMutationOptions<CounselListResponse, CBDCAxiosError, CounselListParams>) => {
const mutation = useMutation<CounselListResponse, CBDCAxiosError, CounselListParams>({
...options,
mutationFn: (params: CounselListParams) => counselList(params),
});
return {
...mutation,
};
};

View File

@@ -1,29 +0,0 @@
import axios from 'axios';
import { API_URL } from '@/shared/api/urls';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import {
CounselSaveParams,
CounselSaveResponse
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const counselSave = (params: CounselSaveParams) => {
return resultify(
axios.post<CounselSaveResponse>(API_URL.counselSave(), params),
);
};
export const useCounselSaveMutation = (options?: UseMutationOptions<CounselSaveResponse, CBDCAxiosError, CounselSaveParams>) => {
const mutation = useMutation<CounselSaveResponse, CBDCAxiosError, CounselSaveParams>({
...options,
mutationFn: (params: CounselSaveParams) => counselSave(params),
});
return {
...mutation,
};
};

View File

@@ -0,0 +1,29 @@
import axios from 'axios';
import { API_URL } from '@/shared/api/urls';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import {
QnaListParams,
QnaListResponse
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const qnaList = (params: QnaListParams) => {
return resultify(
axios.post<QnaListResponse>(API_URL.qnaList(), params),
);
};
export const useQnaListMutation = (options?: UseMutationOptions<QnaListResponse, CBDCAxiosError, QnaListParams>) => {
const mutation = useMutation<QnaListResponse, CBDCAxiosError, QnaListParams>({
...options,
mutationFn: (params: QnaListParams) => qnaList(params),
});
return {
...mutation,
};
};

View File

@@ -0,0 +1,29 @@
import axios from 'axios';
import { API_URL } from '@/shared/api/urls';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import {
QnaSaveParams,
QnaSaveResponse
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const qnaSave = (params: QnaSaveParams) => {
return resultify(
axios.post<QnaSaveResponse>(API_URL.qnaSave(), params),
);
};
export const useQnaSaveMutation = (options?: UseMutationOptions<QnaSaveResponse, CBDCAxiosError, QnaSaveParams>) => {
const mutation = useMutation<QnaSaveResponse, CBDCAxiosError, QnaSaveParams>({
...options,
mutationFn: (params: QnaSaveParams) => qnaSave(params),
});
return {
...mutation,
};
};

View File

@@ -1,4 +1,4 @@
import { DefaulResponsePagination } from '@/entities/common/model/types';
import { DefaulResponsePagination, DefaultRequestPagination } from '@/entities/common/model/types';
export interface SupportParams {
mid?: string;
@@ -6,8 +6,9 @@ export interface SupportParams {
export interface FaqListParams {
category: string;
searchValue: string;
page?: DefaultRequestPagination;
};
export interface FaqListItem {
export interface FaqItem {
sortNo?: string;
seq?: string;
category?: string;
@@ -16,31 +17,34 @@ export interface FaqListItem {
contents?: string;
};
export interface FaqListResponse extends DefaulResponsePagination {
content: Array<FaqListItem>;
content: Array<FaqItem>;
hasNext: boolean;
nextCursor: string | null;
};
export interface FaqItemProps extends FaqListItem {
export interface FaqItemProps extends FaqItem {
}
export interface CounselListParams extends SupportParams {
export interface QnaListParams extends SupportParams {
page?: DefaultRequestPagination;
};
export interface CounselListItemProps {
sortNo: string;
seq: string;
statusCode: string;
statusName: string;
requestDate: string;
requestName: string;
title: string;
contents: string;
answer: string;
export interface QnaItem {
sortNo?: string;
seq?: string;
statusCode?: string;
statusName?: string;
requestDate?: string;
requestName?: string;
title?: string;
contents?: string;
answer?: string;
};
export interface CounselListResponse extends DefaulResponsePagination {
content: Array<CounselListItemProps>
export interface QnaListResponse extends DefaulResponsePagination {
content: Array<QnaItem>
};
export interface CounselSaveParams extends SupportParams {
export interface QnaItemProps extends QnaItem {
};
export interface QnaSaveParams extends SupportParams {
counselType: string;
requestName: string;
requestTel: string;
@@ -48,6 +52,6 @@ export interface CounselSaveParams extends SupportParams {
title: string;
contents: string;
};
export interface CounselSaveResponse {
export interface QnaSaveResponse {
};

View File

@@ -0,0 +1,50 @@
import moment from 'moment';
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { QnaItemProps } from '../model/types';
export const SupportQnaItem = ({
sortNo,
seq,
statusCode,
statusName,
requestDate,
requestName,
title,
contents,
answer
}: QnaItemProps) => {
const { navigate } = useNavigate();
const onClickToDetail = () => {
navigate(PATHS.support.qna.detail, {
state: {
statusCode,
statusName,
requestDate,
requestName,
title,
contents,
answer
}
});
};
return (
<>
<div
className="inq-item"
onClick={ () => onClickToDetail() }
>
<div className="inq-line">
<div className="inq-title-text">{ contents }</div>
<div className="inq-meta">
<span>{ moment(requestDate).format('YYYY.MM.DD') }</span>
<span className="sai"></span> <span>[{ statusName }]</span>
</div>
<span className={`dot ${(statusCode === '03')? 'blue': 'gray'}`}></span>
</div>
</div>
</>
);
};

View File

@@ -4,7 +4,7 @@ import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { HeaderType } from '@/entities/common/model/types';
import { useFaqListMutation } from '@/entities/support/api/use-faq-list-mutation';
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constants';
import { FaqListItem } from '@/entities/support/model/types';
import { FaqItem } from '@/entities/support/model/types';
import { SupportFaqItem } from '@/entities/support/ui/faq-item';
import {
useSetHeaderTitle,
@@ -18,7 +18,7 @@ export const FaqListPage = () => {
const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM);
const [searchValue, setSearchValue] = useState<string>('');
const [resultList, setResultList] = useState<Array<FaqListItem>>([]);
const [resultList, setResultList] = useState<Array<FaqItem>>([]);
useSetHeaderTitle('자주 묻는 질문');
useSetHeaderType(HeaderType.LeftArrow);

View File

@@ -1,18 +1,91 @@
import moment from 'moment';
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 {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
export const QnaDetailPage = () => {
const { navigate } = useNavigate();
const location = useLocation();
const [statusCode, setStatusCode] = useState<string>('');
const [statusName, setStatusName] = useState<string>('');
const [requestDate, setRequestDate] = useState<string>('');
const [requestName, setRequestName] = useState<string>('');
const [title, setTitle] = useState<string>('');
const [contents, setContents] = useState<string>('');
const [answer, setAnswer] = useState<string>('');
useSetHeaderTitle('1:1 문의');
useSetHeaderType(HeaderType.RightClose);
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
useSetOnBack(() => {
navigate(PATHS.support.faq.list);
});
useEffect(() => {
setStatusCode(location?.state.statusCode);
setStatusName(location?.state.statusName);
setRequestDate(location?.state.requestDate);
setRequestName(location?.state.requestName);
setTitle(location?.state.title);
setContents(location?.state.contents);
setAnswer(location?.state.answer);
}, []);
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane active">
<div className="inq-detail">
<div className="inq-detail__head">
<div className="inq-detail__row">
<span className="inq-badge"></span>
<span className="inq-head-text bold"> ...</span>
</div>
<div className="inq-detail__row">
<span className="inq-badge"></span>
<span className="inq-head-text">/ </span>
</div>
<div className="inq-detail__row">
<span className="inq-badge"></span>
<span className="inq-head-text">{ moment(requestDate).format('YYYY.MM.DD') }</span>
</div>
<div className="inq-detail__row">
<span className="inq-badge"></span>
<span className="inq-head-text">2025.07.31</span>
</div>
</div>
<div className="inq-detail__divider"></div>
<div className="inq-detail__section">
<div className="inq-detail__section-title"> </div>
<div className="inq-detail__body">{ answer }</div>
</div>
<div className="inq-detail__section">
<div className="inq-detail__section-title"> </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">{ contents }</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</>
);
};

View File

@@ -1,18 +1,106 @@
import { ChangeEvent, 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 { useQnaListMutation } from '@/entities/support/api/use-qna-list-mutation';
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constants';
import { QnaItem } from '@/entities/support/model/types';
import { SupportQnaItem } from '@/entities/support/ui/qna-item';
import {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
export const QnaListPage = () => {
const { navigate } = useNavigate();
const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM);
const [searchValue, setSearchValue] = useState<string>('');
const [resultList, setResultList] = useState<Array<QnaItem>>([]);
useSetHeaderTitle('1:1 문의');
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(true);
useSetOnBack(() => {
navigate(PATHS.home);
});
const { mutateAsync: qnaList } = useQnaListMutation();
const callList = () => {
let listParams = {
...{page: pageParam}
};
qnaList(listParams).then((rs) => {
console.log(rs)
setResultList(rs.content);
});
};
const getQnaList = () => {
let rs = [];
for(let i=0;i<resultList.length;i++){
rs.push(
<SupportQnaItem
key={ `key-support-faq-item-${i}` }
sortNo={ resultList[i]?.sortNo }
seq={ resultList[i]?.seq }
statusCode={ resultList[i]?.statusCode }
statusName={ resultList[i]?.statusName }
requestDate={ resultList[i]?.requestDate }
requestName={ resultList[i]?.requestName }
title={ resultList[i]?.title }
contents={ resultList[i]?.contents }
answer={ resultList[i]?.answer }
></SupportQnaItem>
)
}
return rs;
};
const onClickToNavigation = () => {
navigate(PATHS.support.qna.register);
};
useEffect(() => {
callList();
}, []);
return (
<>
<main>
<div className="tab-content">
<div className="tab-pane sub active">
<div className="inq117">
<div className="inq-merchant">
<div className="inq-title"></div>
<div className="notice-filter">
<select className="flex-1">
<option></option>
</select>
</div>
</div>
<div className="notice-filter">
<select className="flex-1">
<option></option>
</select>
</div>
<div className="inq-list">
{ getQnaList() }
</div>
</div>
<div className="apply-row bottom-padding">
<button
className="btn-50 btn-blue flex-1"
onClick={ () => onClickToNavigation }
>1:1 </button>
</div>
</div>
</div>
</main>
</>
);
};

View File

@@ -167,11 +167,11 @@ export const API_URL = {
},
/* Counsel Management - 문의상담 API */
counselList: () => {
qnaList: () => {
// POST: 1:1 문의 목록 조회
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/counsel/list`;
},
counselSave: () => {
qnaSave: () => {
// POST: 1:1 문의 등록
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/counsel/save`;
},

View File

@@ -261,6 +261,10 @@ export const PATHS: RouteNamesType = {
`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.qna.base}`,
ROUTE_NAMES.support.qna.detail,
),
register: generatePath(
`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.qna.base}`,
ROUTE_NAMES.support.qna.register,
),
},
},
setting: generatePath(ROUTE_NAMES.setting),

View File

@@ -123,7 +123,8 @@ export const ROUTE_NAMES = {
qna: {
base: '/qna/*',
list: 'list',
detail: 'detail/:qnaId',
detail: 'detail',
register: 'register',
}
},
setting: '/setting',