Skip to content

Commit 2b6c392

Browse files
committed
feat: 구매참가자관리 Excel export 기능 구현
1 parent 34da175 commit 2b6c392

File tree

2 files changed

+58
-6
lines changed

2 files changed

+58
-6
lines changed

src/features/dashboard/api/participants.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,36 @@ export const approveParticipants = async ({ orderId }: { orderId: number }) => {
2020
console.log(error);
2121
}
2222
};
23+
24+
export const downloadExcel = async (eventId:number): Promise<void> => {
25+
const response = await axiosClient.get(
26+
'/host-channels/dashboard/participant-management/excel',
27+
{
28+
responseType: 'blob',
29+
params: { eventId },
30+
}
31+
);
32+
33+
//파일이름 추출
34+
const disposition = response.headers['content-disposition'];
35+
let filename = '같이가요_구매참가자목록.xlsx'; // 기본값
36+
if (disposition) {
37+
const match = disposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
38+
if (match && match[1]) {
39+
filename = decodeURIComponent(match[1].replace(/['"]/g, ''));
40+
}
41+
}
42+
43+
//저장
44+
const blob = new Blob([response.data], {
45+
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
46+
});
47+
const url = window.URL.createObjectURL(blob);
48+
const link = document.createElement('a');
49+
link.href = url;
50+
link.setAttribute('download', filename);
51+
document.body.appendChild(link);
52+
link.click();
53+
link.remove();
54+
window.URL.revokeObjectURL(url);
55+
};

src/pages/dashboard/ui/ParticipantsMangementPage.tsx

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import ParticipantsFilterBar from '../../../widgets/dashboard/ui/ParticipantsFil
77
import EmailModal from '../../../widgets/dashboard/ui/email/EmailModal';
88
import SelectTicketModal from '../../../widgets/dashboard/ui/email/SelectTicketModal';
99
import { useParticipants } from '../../../features/dashboard/hook/useParticipants';
10+
import SecondaryButton from '../../../../design-system/ui/buttons/SecondaryButton';
11+
import { useParams } from 'react-router-dom';
12+
import { downloadExcel } from '../../../features/dashboard/api/participants';
1013

1114
const ParticipantsManagementPage = () => {
1215
const [filterModalOpen, setfilterModalOpen] = useState(false);
@@ -15,6 +18,8 @@ const ParticipantsManagementPage = () => {
1518
const [listType, setListType] = useState<'all' | 'approved' | 'pending'>('all');
1619
const [filter, setFilter] = useState<string[]>([]);
1720
const [searchTerm, setSearchTerm] = useState('');
21+
const { id } = useParams();
22+
const eventId = Number(id);
1823

1924
const { participants } = useParticipants();
2025
const checkedInCount = participants.filter((p: { checkedIn: boolean; }) => p.checkedIn).length;
@@ -29,18 +34,32 @@ const ParticipantsManagementPage = () => {
2934
String(p.ticketId).includes(lowerSearch)
3035
);
3136
});
37+
const exportToExcel = () => {
38+
try {
39+
downloadExcel(eventId);
40+
} catch (error) {
41+
alert('엑셀 다운로드에 실패했습니다.');
42+
console.error(error);
43+
}
44+
}
3245

3346
return (
3447
<DashboardLayout centerContent="DASHBOARD" pinkBg={true}>
3548
<div className="flex flex-col px-2 md:px-4">
3649
<h1 className="text-center font-bold text-xl py-4 md:py-6">구매/참가자 관리</h1>
37-
<div className="flex justify-end gap-2 md:gap-3 px-4">
38-
<h3 className="text-placeholderText text-sm md:text-base">체크인</h3>
39-
<span className="text-sm md:text-base">{checkedInCount}/{participants.length}</span>
40-
<span className="text-sm md:text-base">|</span>
41-
<h3 className="text-placeholderText text-sm md:text-base">미승인</h3>
42-
<span className="text-sm md:text-base">{unapprovedCount}</span>
50+
51+
<div className="flex justify-between items-center px-2">
52+
{<SecondaryButton label="Excel" color="pink" size="small" onClick={exportToExcel} />}
53+
54+
<div className="flex items-center gap-2 md:gap-3">
55+
<h3 className="text-placeholderText text-sm md:text-base">체크인</h3>
56+
<span className="text-sm md:text-base">{checkedInCount}/{participants.length}</span>
57+
<span className="text-sm md:text-base">|</span>
58+
<h3 className="text-placeholderText text-sm md:text-base">미승인</h3>
59+
<span className="text-sm md:text-base">{unapprovedCount}</span>
60+
</div>
4361
</div>
62+
4463
<SearchBar
4564
placeholder="이름, 이메일, 전화번호, 티켓ID로 검색"
4665
className="py-5"

0 commit comments

Comments
 (0)