Skip to content
Merged
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ node_modules/

playwright/.auth

tests/auth/
e2e-tests/auth/
File renamed without changes.
36 changes: 36 additions & 0 deletions e2e-tests/excel/excelDownload.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { paths } from '@/const/paths';
import test, { expect } from '@playwright/test';

test.use({ storageState: 'e2e-tests/auth/admin.json' });

test.describe('엑셀 다운로드 테스트', () => {
test('스터디 관리 페이지 (manage-study) 엑셀 다운로드 테스트', async ({ page }) => {
await page.goto(paths.admin.manageStudy);

await page.waitForTimeout(3000);
const [download] = await Promise.all([
page.waitForEvent('download'),
page.getByRole('button', { name: '그룹 활동 목록 엑셀 다운' }).click(),
]);

expect(download.suggestedFilename()).toBe('스터디그룹활동.xlsx');

const failure = await download.failure();
expect(failure).toBeNull();
});

test('스터디 신청자 목록 페이지 (manage-student) 엑셀 다운로드 테스트', async ({ page }) => {
await page.goto(paths.admin.manageStudent);
await page.waitForTimeout(3000);

const [download] = await Promise.all([
page.waitForEvent('download'),
page.getByRole('button', { name: '신청자 목록 다운로드' }).click(),
]);

expect(download.suggestedFilename()).toBe('스터디신청자목록.xlsx');

const failure = await download.failure();
expect(failure).toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function formatMinutesToHoursAndMinutes(totalMinutes: string | number): s
return `${hours}시간 ${remainingMinutes}분`;
}

test.use({ storageState: 'tests/auth/member.json' });
test.use({ storageState: 'e2e-tests/auth/member.json' });

test.describe('스터디원 리포트 테스트', () => {
// 업로드한 이미지와 실제 올라간 이미지를 구분 못하는 이슈 존재
Expand Down
3 changes: 2 additions & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ if (!isCI) {
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
timeout: 120_000,
testDir: './e2e-tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
Expand Down
15 changes: 7 additions & 8 deletions src/pages/Admin/ManageStudent/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useQuery } from 'react-query';
import { StudyApplyUser } from '@/interface/user';
import SpinnerLoading from '@/components/SpinnerLoading';
import { toast } from 'sonner';
import { downloadExcelFromSheetData } from '@/utils/excel';

// Helper function to clean course names
const cleanCourseName = (name: string) => name.replace(/\n/g, ' ');
Expand Down Expand Up @@ -86,8 +87,12 @@ export default function ManageStudentPage() {
}, [applicants, searchTerm]);

const handleExcelDownload = () => {
if (applicants) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildApplicantsSheetData 함수에서 applicants의 존재 유무를 확인한다면 기존 로직과 다르게 동작할 것 같습니다.
handleExcelDownload 함수 상단에서 처리해주는 건 어떨까요?

Suggested change
if (applicants) {
const handleExcelDownload = () => {
if(applicants) return ;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bb95559 에서 수정하였습니다.

const sheetData = applicants.map((student) => ({
if (!applicants) return;

downloadExcelFromSheetData(buildApplicantsSheetData(applicants), '스터디신청자목록.xlsx');

function buildApplicantsSheetData(applicants: StudyApplyUser[]) {
return applicants.map((student) => ({
ID: student.id,
Name: student.name,
StudentId: student.sid,
Expand All @@ -96,12 +101,6 @@ export default function ManageStudentPage() {
Courses: student.courses.map((subject) => subject.name + `(${subject.prof})`).join(', '),
Friends: student.friends.map((friend) => friend.name).join(', '),
}));

const ws = xlsx.utils.json_to_sheet([...sheetData]);
const wb = xlsx.utils.book_new();
xlsx.utils.book_append_sheet(wb, ws, 'Sheet1');

xlsx.writeFile(wb, '스터디신청자목록.xlsx');
}
};

Expand Down
26 changes: 13 additions & 13 deletions src/pages/Admin/ManageStudy/Page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as xlsx from 'xlsx';
import * as React from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
Expand All @@ -10,6 +9,8 @@ import { useQuery } from 'react-query';
import SpinnerLoading from '@/components/SpinnerLoading';
import { Link } from 'react-router-dom';
import { paths } from '@/const/paths';
import { downloadExcelFromSheetData } from '@/utils/excel';
import { Group } from '@/interface/group';

export default function ManageStudyPage() {
const { data: activities, isLoading } = useQuery(['courses'], readAllGroups, {
Expand All @@ -33,9 +34,13 @@ export default function ManageStudyPage() {
}, [activities, searchTerm]);

const handleExcelDownload = () => {
if (activities) {
const sheetData = activities.flatMap((group) =>
group.members.map((member) => ({
if (!activities) return;

downloadExcelFromSheetData(buildStudySheetData(activities), '스터디그룹활동.xlsx');

function buildStudySheetData(activities: Group[]) {
return activities.flatMap((group) => {
const sheetRow = group.members.map((member) => ({
Group: group.group,
MemberID: member.id,
MemberName: member.name,
Expand All @@ -44,15 +49,10 @@ export default function ManageStudyPage() {
Subjects: member.courses.map((subject) => subject.name).join(', '),
Reports: group.reports,
Times: group.times,
})),
);
const ws = xlsx.utils.json_to_sheet([...sheetData]);
const wb = xlsx.utils.book_new();
xlsx.utils.book_append_sheet(wb, ws, 'Sheet1');
}));

xlsx.writeFile(wb, '스터디그룹활동.xlsx');
} else {
console.log('데이터가 비어있습니다.');
return sheetRow;
});
}
};

Expand All @@ -71,7 +71,7 @@ export default function ManageStudyPage() {
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<Button onClick={handleExcelDownload}>
<Button onClick={activities && handleExcelDownload}>
<DownloadIcon className="mr-2 h-4 w-4" />
그룹 활동 목록 엑셀 다운
</Button>
Expand Down
16 changes: 16 additions & 0 deletions src/utils/excel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as xlsx from 'xlsx';

export function downloadExcelFromSheetData<T>(sheetData: T[], filename: string) {
const workBook = buildWorkBook(sheetData);
xlsx.writeFile(workBook, filename);

function buildWorkBook<T>(sheetData: T[]) {
const workBook = xlsx.utils.book_new();

const workSheet = xlsx.utils.json_to_sheet(sheetData);

xlsx.utils.book_append_sheet(workBook, workSheet);

return workBook;
}
}