Skip to content

Commit

Permalink
Merge pull request #270 from CheckMate-sookmyung/feat/#234-detail-stat
Browse files Browse the repository at this point in the history
#234 feat: 세부 통계 페이지 임시 생성 후 더미데이터로 구현
  • Loading branch information
misung-dev authored Sep 11, 2024
2 parents 2cbb50a + b26eb23 commit 925ba62
Show file tree
Hide file tree
Showing 9 changed files with 539 additions and 0 deletions.
36 changes: 36 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.8",
"chart.js": "^4.4.4",
"chartjs-plugin-datalabels": "^2.2.0",
"craco-alias": "^3.0.1",
"date-fns": "^3.6.0",
"moment": "^2.30.1",
Expand All @@ -17,6 +19,7 @@
"react-animated-numbers": "^0.18.0",
"react-app-alias": "^2.2.2",
"react-calendar": "^5.0.0",
"react-chartjs-2": "^5.2.0",
"react-datepicker": "^7.2.0",
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
Expand Down
4 changes: 4 additions & 0 deletions src/components/TopNavigation/TopNavigation.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export default function TopNavigation({ eventTitle } = {}) {
<S.Menu to="/stats" activeClassName="active">
통계
</S.Menu>
{/* 세부 통계 페이지는 추후 통째로 삭제 */}
<S.Menu to="/stats/detail" activeClassName="active">
세부 통계(임시)
</S.Menu>
{location.pathname.startsWith('/event/dashboard') && (
<S.PageNameWrapper>
{eventTitle !== undefined && (
Expand Down
228 changes: 228 additions & 0 deletions src/pages/DetailStatisticsPage/DetailStatisticsPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import * as S from './DetailStatisticsPage.style';
import { PageLayout } from '@/Layout';
import { TopNavigation } from '@/components';
import { Doughnut } from 'react-chartjs-2';
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { ATTENDEE_LIST } from './attendee';

ChartJS.register(ArcElement, Tooltip, Legend, ChartDataLabels);

const DetailStatisticsPage = () => {
const startDate = ATTENDEE_LIST[0].eventSchedules[0].date.split('T')[0];
const endDate =
ATTENDEE_LIST[0].eventSchedules[
ATTENDEE_LIST[0].eventSchedules.length - 1
].date.split('T')[0];

// 학과별 참석 비율을 계산하는 로직
const departmentAttendance = {};
ATTENDEE_LIST[0].eventSchedules.forEach((schedule) => {
schedule.students.forEach((student) => {
if (student.isAttending) {
if (departmentAttendance[student.major]) {
departmentAttendance[student.major]++;
} else {
departmentAttendance[student.major] = 1;
}
}
});
});

const sortedDepartmentAttendance = Object.entries(departmentAttendance).sort(
(a, b) => b[1] - a[1],
);

const majorAttendanceLimit = 4;
const departmentLabels = sortedDepartmentAttendance
.slice(0, majorAttendanceLimit)
.map((item) => item[0]);

const etcValue = sortedDepartmentAttendance
.slice(majorAttendanceLimit)
.reduce((sum, item) => sum + item[1], 0);

if (etcValue > 0) {
departmentLabels.push('기타');
}

const departmentValues = sortedDepartmentAttendance
.slice(0, majorAttendanceLimit)
.map((item) => item[1]);

if (etcValue > 0) {
departmentValues.push(etcValue);
}

const departmentColors = departmentValues.map((_, index) => {
if (index < 4) {
return ['#2F7CEF', '#ACCDFF', '#2f7cef33', '#EDF5FF'][index];
} else {
return '#E4E4E4';
}
});

const departmentData = {
labels: departmentLabels,
datasets: [
{
data: departmentValues,
backgroundColor: departmentColors,
},
],
};

// 학번별 참석 비율을 계산하는 로직
const yearAttendance = {};
ATTENDEE_LIST[0].eventSchedules.forEach((schedule) => {
schedule.students.forEach((student) => {
if (student.isAttending) {
if (yearAttendance[student.studentYear]) {
yearAttendance[student.studentYear]++;
} else {
yearAttendance[student.studentYear] = 1;
}
}
});
});

const sortedYearAttendance = Object.entries(yearAttendance).sort(
(a, b) => b[0] - a[0],
);

const yearAttendanceLimit = 4;
const yearLabels = sortedYearAttendance
.slice(0, yearAttendanceLimit)
.map((item) => `${item[0]}학번`);

const etcYearValue = sortedYearAttendance
.slice(yearAttendanceLimit)
.reduce((sum, item) => sum + item[1], 0);

if (etcYearValue > 0) {
yearLabels.push('기타');
}

const yearValues = sortedYearAttendance
.slice(0, yearAttendanceLimit)
.map((item) => item[1]);

if (etcYearValue > 0) {
yearValues.push(etcYearValue);
}

const yearColors = yearValues.map((_, index) => {
if (index < 4) {
return ['#2F7CEF', '#ACCDFF', '#2f7cef33', '#EDF5FF'][index];
} else {
return '#E4E4E4';
}
});

const yearData = {
labels: yearLabels,
datasets: [
{
data: yearValues,
backgroundColor: yearColors,
},
],
};

// 이수율 계산 로직
const totalStudents = ATTENDEE_LIST[0].eventSchedules.reduce(
(total, schedule) => total + schedule.students.length,
0,
);

const attendingStudents = ATTENDEE_LIST[0].eventSchedules.reduce(
(total, schedule) =>
total + schedule.students.filter((student) => student.isAttending).length,
0,
);

const completionRate = ((attendingStudents / totalStudents) * 100).toFixed(0);
const nonCompletionRate = (100 - completionRate).toFixed(0);

// 이수율 차트 데이터
const completionData = {
labels: ['이수', '미이수'],
datasets: [
{
data: [completionRate, nonCompletionRate],
backgroundColor: ['#2F7CEF', '#ACCDFF'],
},
],
};

const options = {
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
padding: 20,
boxWidth: 10,
},
},
datalabels: {
color: '#000',
anchor: 'center',
align: 'center',
textAlign: 'center',

formatter: (value, context) => {
const total = context.dataset.data.reduce((acc, val) => acc + val, 0);
const percentage = ((value / total) * 100).toFixed(0);
const label = context.chart.data.labels[context.dataIndex];
return `${label} \n ${percentage}%`;
},
},
},
layout: {
padding: {
right: 10,
},
},
};

return (
<PageLayout topNavigation={<TopNavigation />}>
<S.Container>
<S.DetailStatisticsPage>
<S.TopContainer>
<S.Title>행사별 통계</S.Title>
<S.EventDate>
{startDate} ~ {endDate}
</S.EventDate>
</S.TopContainer>

<S.ContentContainer>
<S.ChartWrapper>
<S.ChartTitle>행사에 참석한 학과 비율</S.ChartTitle>
<S.Chart>
<Doughnut data={departmentData} options={options} />
</S.Chart>
</S.ChartWrapper>

<S.ChartWrapper>
<S.ChartTitle>각 학번별 참석률</S.ChartTitle>
<S.Chart>
<Doughnut data={yearData} options={options} />
</S.Chart>
</S.ChartWrapper>

<S.ChartWrapper>
<S.ChartTitle>전체 학생 중 이수율</S.ChartTitle>
<S.Chart>
<Doughnut data={completionData} options={options} />
</S.Chart>
</S.ChartWrapper>
</S.ContentContainer>
</S.DetailStatisticsPage>
</S.Container>
</PageLayout>
);
};

export default DetailStatisticsPage;
Loading

0 comments on commit 925ba62

Please sign in to comment.