Skip to content

Commit

Permalink
#247 feat: 참석률 높은 행사 TOP 3 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
hanjeonghyun committed Sep 23, 2024
1 parent 4917ff7 commit 1d729b8
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 91 deletions.
2 changes: 1 addition & 1 deletion src/components/GraphBox/GraphBox.style.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const ChartWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
width: calc(50% - 16px);
width: calc(60% - 16px);
@media (max-width: ${BREAKPOINTS[1]}px) {
width: 100%;
Expand Down
121 changes: 121 additions & 0 deletions src/pages/TotalStatisticsPage/EventsGraphChart.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React, { useEffect, useState } from 'react';
import { Doughnut } from 'react-chartjs-2';
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
import * as S from './EventsGraphChart.style';
import { axiosInstance } from '@/axios';

ChartJS.register(ArcElement, Tooltip, Legend);

const EventsGraphChart = () => {
const [rankedEvents, setRankedEvents] = useState([]);

useEffect(() => {
const fetchStatistics = async () => {
try {
const eventsResponse = await axiosInstance.get(
`/api/v1/events/statistic/event`,
);

const top3Events = eventsResponse.data.slice(0, 3);

const coloredEvents = top3Events.map((event, index) => {
let color = '';
switch (index) {
case 0:
color = '#333';
break;
case 1:
color = '#1E88E5';
break;
case 2:
color = '#4CAF50';
break;
default:
color = '#E4E4E4';
}
return { ...event, color };
});

setRankedEvents(coloredEvents);
} catch (error) {
console.error('Error fetching statistics:', error);
}
};

fetchStatistics();
}, []);

const createEventDoughnutData = (rating, color) => ({
labels: ['참석', '미참석'],
datasets: [
{
data: [Math.floor(rating), Math.floor(100 - rating)],
backgroundColor: [color, '#E0E0E0'],
hoverBackgroundColor: [color, '#E0E0E0'],
borderWidth: 0,
},
],
});

const doughnutOptions = {
responsive: true,
maintainAspectRatio: false,
cutout: '30%',
plugins: {
legend: {
display: false,
position: 'right',
},
tooltip: {
callbacks: {
label: (tooltipItem) => {
const label = tooltipItem.label || '';
const value = tooltipItem.raw || 0;
return `${label}: ${value}%`;
},
},
},
datalabels: {
color: '#000',
anchor: 'center',
align: 'center',
textAlign: 'center',
formatter: (value, context) => {
if (typeof value === 'number') {
const percentage = value.toFixed(0);
const label = context.chart.data.labels[context.dataIndex];
return `${label} \n ${percentage}%`;
}
return '';
},
},
},
};

return (
<S.GraphContainer>
{rankedEvents.map((event, index) => (
<S.GraphBoxWrapper key={event.eventTitle}>
<S.LeftSide>
<S.TopBadge>TOP {index + 1}</S.TopBadge>
<S.EventTitle>{event.eventTitle}</S.EventTitle>
<S.AttendanceInfo>
<S.AttendancePercentage>
{Math.floor(event.eventRating)}%
</S.AttendancePercentage>
<S.AttendanceLabel>참석</S.AttendanceLabel>
</S.AttendanceInfo>
</S.LeftSide>
<S.ChartWrapper>
<Doughnut
data={createEventDoughnutData(event.eventRating, event.color)}
options={doughnutOptions}
/>
</S.ChartWrapper>
</S.GraphBoxWrapper>
))}
</S.GraphContainer>
);
};

export default EventsGraphChart;
86 changes: 86 additions & 0 deletions src/pages/TotalStatisticsPage/EventsGraphChart.style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import styled from 'styled-components';

export const GraphContainer = styled.div`
display: flex;
justify-content: space-around;
padding: 30px 0;
gap: 30px;
@media (max-width: 768px) {
flex-direction: column;
gap: 20px;
}
`;

export const GraphBoxWrapper = styled.div`
flex: 1;
display: flex;
border: 1px solid #aecfff;
border-radius: 20px;
padding: 30px;
position: relative;
max-width: 400px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
@media (max-width: 768px) {
flex-direction: column;
align-items: center;
}
`;

export const LeftSide = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
flex: 1;
`;

export const TopBadge = styled.div`
position: absolute;
top: -20px;
right: 10%;
background-color: #e3f2fd;
color: #1e88e5;
padding: 10px 30px;
border-radius: 18px;
font-size: 20px;
font-weight: bold;
border: 1px solid #90caf9;
transform: translateX(20%);
`;

export const EventTitle = styled.h3`
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 10px;
text-align: left;
`;

export const AttendanceInfo = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 5px;
`;

export const AttendancePercentage = styled.div`
font-size: 36px;
font-weight: bold;
color: #333;
`;

export const AttendanceLabel = styled.div`
font-size: 20px;
font-weight: 600;
color: #323232;
`;

export const ChartWrapper = styled.div`
width: 150px;
height: 150px;
display: flex;
justify-content: center;
align-items: center;
`;
118 changes: 31 additions & 87 deletions src/pages/TotalStatisticsPage/GraphChart.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useContext } from 'react';
import { Bar } from 'react-chartjs-2';
import React, { useContext, useEffect, useState } from 'react';
import { Bar, Pie } from 'react-chartjs-2';
import { SortedStudent } from './TotalStatisticsPage';
import {
Chart as ChartJS,
Expand All @@ -9,10 +9,11 @@ import {
Legend as ChartLegend,
CategoryScale,
LinearScale,
ArcElement,
} from 'chart.js';
import { GraphBox } from '@/components';
import styled from 'styled-components';
import { BsThreeDots } from 'react-icons/bs';
import * as S from './GraphChart.style';
import EventsGraphChart from './EventsGraphChart';

ChartJS.register(
BarElement,
Expand All @@ -21,84 +22,20 @@ ChartJS.register(
ChartLegend,
CategoryScale,
LinearScale,
ArcElement,
);

const ChartContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
padding: 30px 0;
`;

const ChartArea = styled.div`
display: flex;
flex: 1;
min-width: 300px;
max-width: 600px;
padding-right: 40px;
`;

const LegendContainer = styled.div`
display: flex;
flex-direction: column;
justify-content: space-around;
gap: 10px;
`;

const LegendItem = styled.div`
display: flex;
align-items: center;
font-size: 16px;
color: #323232;
gap: 10px;
`;

const LegendCircle = styled.div`
width: 16px;
height: 16px;
background-color: ${(props) => props.color};
border-radius: 50%;
margin-right: 10px;
`;

const LegendText = styled.span`
color: #323232;
flex-grow: 1;
`;

const LegendDots = styled(BsThreeDots)`
font-size: 16px;
color: #5495f6;
display: flex;
align-items: center;
`;

const LegendPercentage = styled.span`
font-size: 16px;
color: #2f7cef;
display: flex;
align-items: center;
`;

const HiddenLegend = styled.div`
display: block;
@media (max-width: 1024px) {
display: none;
}
`;

const Legend = ({ topStudents, ratedColors }) => (
<LegendContainer>
<S.LegendContainer>
{topStudents.map((student, index) => (
<LegendItem key={student.studentName}>
<LegendCircle color={ratedColors[index]} />
<LegendText>{student.studentName}</LegendText>
<LegendDots />
<LegendPercentage>{`(${student.attendanceRate}%)`}</LegendPercentage>
</LegendItem>
<S.LegendItem key={student.studentName}>
<S.LegendCircle color={ratedColors[index]} />
<S.LegendText>{student.studentName}</S.LegendText>
<S.LegendDots />
<S.LegendPercentage>{`(${Math.floor(student.attendanceRate)}%)`}</S.LegendPercentage>
</S.LegendItem>
))}
</LegendContainer>
</S.LegendContainer>
);

const GraphChart = () => {
Expand Down Expand Up @@ -126,6 +63,7 @@ const GraphChart = () => {
data: attendanceRates,
backgroundColor: ratedColors,
borderWidth: 1,
borderRadius: 10,
datalabels: {
color: '#fff',
align: 'end',
Expand Down Expand Up @@ -193,16 +131,22 @@ const GraphChart = () => {
};

return (
<GraphBox title="출석률 좋은 학생 TOP 5">
<ChartContent>
<ChartArea>
<Bar data={graphData} options={options} />
</ChartArea>
<HiddenLegend>
<Legend topStudents={topStudents} ratedColors={ratedColors} />
</HiddenLegend>
</ChartContent>
</GraphBox>
<div>
<GraphBox title="출석률 좋은 학생 TOP 5">
<S.ChartContent>
<S.ChartArea>
<Bar data={graphData} options={options} />
</S.ChartArea>
<S.HiddenLegend>
<Legend topStudents={topStudents} ratedColors={ratedColors} />
</S.HiddenLegend>
</S.ChartContent>
</GraphBox>
<S.MarginBox>
<S.ChartTitle>출석률 높은 행사 TOP 3</S.ChartTitle>
<EventsGraphChart />
</S.MarginBox>
</div>
);
};

Expand Down
Loading

0 comments on commit 1d729b8

Please sign in to comment.