Skip to content

Commit a1bde0c

Browse files
committed
refact: query 페이지 삭제 및 응답관리 페이지 수정"
1 parent 904031e commit a1bde0c

File tree

7 files changed

+53
-101
lines changed

7 files changed

+53
-101
lines changed

src/features/dashboard/ui/MultiplePieCharts.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ const MultiplePieCharts = ({ responses }: MultiplePieChartsProps) => {
2626
<div className="grid grid-cols-1 gap-6 p-4">
2727
{responses.map((option, _index) => {
2828
const data = aggregateAnswers(option.answers);
29-
const allSame = data.length === 1;
3029

3130
return (
3231
<div key={option.optionId} className="flex flex-col items-center">
@@ -41,7 +40,7 @@ const MultiplePieCharts = ({ responses }: MultiplePieChartsProps) => {
4140
cy="50%"
4241
outerRadius={80}
4342
label
44-
stroke={allSame ? "none" : "white"}
43+
stroke="none"
4544
>
4645
{data.map((_, i) => (
4746
<Cell key={`cell-${i}`} fill={COLORS[i % COLORS.length]} />

src/features/dashboard/ui/ResponseFilter.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import leftButton from '../../../../public/assets/main/LeftButton.svg';
77

88
interface ResponseFilterProps {
99
responses: responsesData[];
10-
listType: 'summary' | 'query' | 'individual';
10+
listType: 'summary' | 'individual';
1111
selectedField: { v1: string; v2: string };
1212
setSelectedField: (v1: string, v2: string) => void;
1313
setCurrentIndex: (updateFn: (prevIndex: number) => number) => void;
@@ -50,8 +50,7 @@ const ResponseFilter = ({
5050
selectedValue={selectedField.v1}
5151
onSelect={(selectedName, selectedEmail) => {
5252
const selectedOption = options.find(opt =>
53-
(listType === 'query' ? fieldMapToKorean[opt.v1] === selectedName : opt.v1 === selectedName) &&
54-
opt.v2 === selectedEmail
53+
opt.v1 === selectedName && opt.v2 === selectedEmail
5554
);
5655
if (selectedOption) {
5756
setSelectedField(selectedOption.v1, selectedOption.v2);

src/features/dashboard/ui/ResponsesList.tsx

Lines changed: 1 addition & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { useResponseStore } from '../model/store/ResponseStore';
2-
import { responsesInfo } from '../../../shared/types/responseType';
32
import ResponseFilter from './ResponseFilter';
43
import SelectedResponseList from './SelectedResponseList';
5-
import { createFieldMappings } from '../../../shared/lib/createFieldMappings';
64
import { useEffect } from 'react';
75
import { TicketOptionAnswerResponse } from '../../ticket/model/ticketInformation';
86
import MultiplePieCharts from './MultiplePieCharts';
@@ -15,58 +13,16 @@ interface ResponsesListProps {
1513
const ResponsesList = ({ listType, ticketOptionResponses }: ResponsesListProps) => {
1614
const {
1715
response,
18-
selectedField,
19-
setSelectedField,
2016
selectedResponse,
2117
setSelectedResponse,
2218
currentIndex,
2319
setCurrentIndex,
2420
} = useResponseStore();
25-
const { fieldMap, fieldMapToKorean } = createFieldMappings(response);
26-
const queryOptions =
27-
response && response[0]
28-
? Object.keys(response[0])
29-
.filter(key => key !== 'id' && key !== 'selectedOptions')
30-
.map(key => ({
31-
v1: fieldMap[key] || key,
32-
v2: '',
33-
}))
34-
: [];
3521

3622
useEffect(() => {
3723
setCurrentIndex(() => 0);
3824
}, [listType, setCurrentIndex]);
3925

40-
const renderSection = (title: string, key: keyof (typeof responsesInfo)[0], isSummaryPage: boolean) => {
41-
const transTitle = fieldMapToKorean[title];
42-
43-
return (
44-
<div className="bg-white p-4 flex flex-col gap-2 mb-4">
45-
<div className="flex justify-between items-center text-xs bg-white px-2 md:px-3 py-3">
46-
<p className="text-base font-bold">{transTitle}</p>
47-
<p>응답 {response.length}</p>
48-
</div>
49-
50-
{response.length === 0 ? (
51-
<p>응답이 없습니다.</p>
52-
) : (
53-
<div
54-
className={isSummaryPage ? 'h-full max-h-48 overflow-y-auto space-y-2' : 'h-full overflow-y-auto space-y-2'}
55-
>
56-
{response.map(response => (
57-
<div
58-
className="flex justify-between text-xs bg-gray-100 shadow-sm px-2 md:px-3 py-3 gap-2"
59-
key={response.id}
60-
>
61-
<p>{typeof response[key] === 'object' ? JSON.stringify(response[key]) : response[key]}</p>
62-
</div>
63-
))}
64-
</div>
65-
)}
66-
</div>
67-
);
68-
};
69-
7026
const renderList = () => {
7127
switch (listType) {
7228
case 'summary':
@@ -80,22 +36,7 @@ const ResponsesList = ({ listType, ticketOptionResponses }: ResponsesListProps)
8036
</div>
8137
</>
8238
);
83-
case 'query':
84-
return (
85-
<>
86-
<ResponseFilter
87-
responses={response}
88-
listType={listType}
89-
selectedField={{ v1: fieldMapToKorean[selectedField], v2: '' }}
90-
setSelectedField={setSelectedField}
91-
setCurrentIndex={setCurrentIndex}
92-
currentIndex={currentIndex}
93-
responsesLength={queryOptions.length}
94-
options={queryOptions}
95-
/>
96-
{renderSection(selectedField, selectedField as keyof (typeof responsesInfo)[0], false)}
97-
</>
98-
);
39+
9940
case 'individual':
10041
return (
10142
<div>

src/pages/dashboard/ui/ResponseManagementPage.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,32 @@ import DashboardLayout from '../../../shared/ui/backgrounds/DashboardLayout';
33
import ResponsesFilterBar from '../../../widgets/dashboard/ui/ResponsesFilterBar';
44
import ResponsesList from '../../../features/dashboard/ui/ResponsesList';
55
import { useResponseStore } from '../../../features/dashboard/model/store/ResponseStore';
6-
import { responsesInfo } from '../../../shared/types/responseType';
76
import { useLocation } from 'react-router-dom';
87
import ResponesModal from '../../../widgets/dashboard/ui/response/ResponseModal';
98
import { usePurchaserAnswers } from '../../../features/ticket/hooks/useTicketOptionHook';
109

1110
const ResponseManagementPage = () => {
12-
const [listType, setListType] = useState<'summary' | 'query' | 'individual'>('summary');
13-
const { response, setResponses, setSelectedResponse, isModalOpen, closeModal, selectedTicketId } = useResponseStore();
11+
const [listType, setListType] = useState<'summary' | 'individual'>('summary');
12+
const { setSelectedResponse, isModalOpen, closeModal, selectedTicketId } = useResponseStore();
1413
const { data } = usePurchaserAnswers(selectedTicketId);
1514
console.log(data)
1615
const location = useLocation();
1716
const { participantName, participantEmail } = location.state || {};
18-
useEffect(() => {
19-
setResponses(responsesInfo);
20-
}, [setResponses]);
17+
2118
useEffect(() => {
2219
if (participantName) {
2320
setListType('individual');
2421
setSelectedResponse(participantName, participantEmail);
2522
}
2623
}, [participantName]);
24+
2725
return (
2826
<DashboardLayout centerContent="WOOACON 2024" pinkBg={true}>
2927
{isModalOpen && (
3028
<ResponesModal onClose={closeModal}></ResponesModal>
3129
)}
3230
<div className="flex flex-col px-2 md:px-4">
33-
<h1 className="text-left font-semibold md:text-2xl text-xl py-4 md:py-6 pl-4">응답 {response.length}</h1>
31+
<h1 className="text-left font-semibold md:text-2xl text-xl py-4 md:py-6 pl-4">응답 {data?.result.length}</h1>
3432
<div className="flex justify-center">
3533
<ResponsesFilterBar listType={listType} setListType={setListType} />
3634
</div>

src/pages/dashboard/ui/ticket/TicketOptionResponsePage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const TicketOptionResponsePage = () => {
1515
if (!data?.isSuccess) return <div>옵션 정보를 불러오지 못했습니다.</div>;
1616

1717
return (
18-
<TicketOptionLayout ticketAmount={ticketCnt} ticketInfo={{ ticketId, eventId, ticketCnt }}>
18+
<TicketOptionLayout ticketAmount={ticketCnt} ticketInfo={{ ticketId, eventId, ticketCnt }} options={data?.result}>
1919
<TicketOption options={data?.result}>
2020
</TicketOption>
2121
</TicketOptionLayout>

src/shared/ui/backgrounds/TicketOptionLayout.tsx

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ import { useOrderTicket } from "../../../features/ticket/hooks/useOrderHook";
99
import { OrderTicketRequest } from "../../../features/ticket/model/orderInformation";
1010
import { useTickets } from "../../../features/ticket/hooks/useTicketHook";
1111
import { useCreateTicketOptionAnswers } from "../../../features/ticket/hooks/useTicketOptionHook";
12+
import { TicketOptionResponse } from "../../../features/ticket/model/ticketInformation";
1213

1314
interface TicketOptionLayoutProps {
1415
children: React.ReactNode;
1516
ticketAmount: number;
1617
ticketInfo: OrderTicketRequest;
18+
options: TicketOptionResponse[];
1719
}
1820

19-
const TicketOptionLayout = ({ children, ticketAmount, ticketInfo }: TicketOptionLayoutProps) => {
21+
const TicketOptionLayout = ({ children, ticketAmount, ticketInfo, options }: TicketOptionLayoutProps) => {
2022
const navigate = useNavigate();
2123
const { currentPage, setCurrentPage, selectedOptions, resetOptions } = useTicketOptionStore();
2224
const centerContent = `티켓 옵션 선택 (${currentPage}/${ticketAmount})`;
@@ -27,6 +29,7 @@ const TicketOptionLayout = ({ children, ticketAmount, ticketInfo }: TicketOption
2729
(ticket) => ticket.ticketId === ticketInfo.ticketId
2830
);
2931
const { mutate: submitAnswers } = useCreateTicketOptionAnswers();
32+
console.log(selectedOptions)
3033

3134
//페이지
3235
const pageIndicator = Array(ticketAmount).fill(" . ");
@@ -35,7 +38,28 @@ const TicketOptionLayout = ({ children, ticketAmount, ticketInfo }: TicketOption
3538
//버튼 텍스트
3639
const isLastPage = currentPage === ticketAmount;
3740
const buttonText = isLastPage ? "결제하기" : "다음 티켓 옵션 선택하기";
41+
3842
const handleNextPage = () => {
43+
const currentOptions = selectedOptions[currentPage];
44+
const requiredOptions = options.filter((opt) => opt.isMandatory);
45+
const isValid = requiredOptions.every((opt) => {
46+
const answer = currentOptions?.[opt.id];
47+
if (opt.type === "TEXT") {
48+
return typeof answer === "string" && answer.trim() !== "";
49+
}
50+
if (opt.type === "SINGLE") {
51+
return typeof answer === "number";
52+
}
53+
if (opt.type === "MULTIPLE") {
54+
return Array.isArray(answer) && answer.length > 0;
55+
}
56+
return false;
57+
});
58+
if (!isValid) {
59+
alert("필수 옵션을 모두 입력해주세요.");
60+
return;
61+
}
62+
3963
if (isLastPage) {
4064
const sendAnswersByPage = async () => {
4165
for (const pageIndex of Object.keys(selectedOptions).sort((a, b) => Number(a) - Number(b))) {
@@ -73,11 +97,7 @@ const TicketOptionLayout = ({ children, ticketAmount, ticketInfo }: TicketOption
7397
onSuccess: (response) => {
7498
if (response.isSuccess && Array.isArray(response.result)) {
7599
const orderIds = response.result;
76-
navigate('/payment/ticket-confirm', {
77-
state: { orderIds, ...ticketInfo }
78-
});
79-
} else {
80-
alert("주문 정보를 불러올 수 없습니다.");
100+
navigate('/payment/ticket-confirm', { state: { orderIds } });
81101
}
82102
},
83103
});
@@ -128,23 +148,23 @@ const TicketOptionLayout = ({ children, ticketAmount, ticketInfo }: TicketOption
128148
</div>
129149
</div>
130150

131-
<div className="flex flex-col mt-8 mx-auto w-[85%]">
132-
<p className="font-bold text-sm md:text-base">추가 옵션</p>
133-
<p className="text-xs md:text-sm text-gray-500 mt-2">
134-
구매하는 티켓에 추가적으로 선택할 수 있는 옵션들이 있습니다.
135-
</p>
136-
</div>
151+
<div className="flex flex-col mt-8 mx-auto w-[85%]">
152+
<p className="font-bold text-sm md:text-base">추가 옵션</p>
153+
<p className="text-xs md:text-sm text-gray-500 mt-2">
154+
구매하는 티켓에 추가적으로 선택할 수 있는 옵션들이 있습니다.
155+
</p>
156+
</div>
137157

138-
{/* 내용 영역 */}
139-
<div className="flex flex-col w-[85%] mx-auto mt-4">
140-
{children}
141-
<div className="flex flex-grow" />
142-
<div className="w-full p-6">
143-
<Button label={buttonText} onClick={handleNextPage} className="w-full h-12 rounded-full" />
158+
{/* 내용 영역 */}
159+
<div className="flex flex-col w-[85%] mx-auto mt-4">
160+
{children}
161+
<div className="flex flex-grow" />
162+
<div className="w-full p-6">
163+
<Button label={buttonText} onClick={handleNextPage} className="w-full h-12 rounded-full" />
164+
</div>
165+
</div>
144166
</div>
145-
</div>
146-
</div>
147-
);
167+
);
148168
};
149169

150170
export default TicketOptionLayout;

src/widgets/dashboard/ui/ResponsesFilterBar.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import TextButton from '../../../../design-system/ui/buttons/TextButton';
22

33
interface ResponsesFilterBarProps {
4-
listType: 'summary' | 'query' | 'individual';
5-
setListType: (type: 'summary' | 'query' | 'individual') => void;
4+
listType: 'summary' | 'individual';
5+
setListType: (type: 'summary' | 'individual') => void;
66
}
77

88
const ResponsesFilterBar = ({
@@ -16,11 +16,6 @@ const ResponsesFilterBar = ({
1616
onClick={() => setListType('summary')}
1717
className={listType === 'summary' ? 'text-main' : ''}
1818
/>
19-
<TextButton
20-
label="질문"
21-
onClick={() => setListType('query')}
22-
className={listType === 'query' ? 'text-main' : ''}
23-
/>
2419
<TextButton
2520
label="개별 조회"
2621
onClick={() => setListType('individual')}

0 commit comments

Comments
 (0)