Skip to content

Commit

Permalink
#247 feat: 그래프 차트 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
hanjeonghyun committed Sep 17, 2024
1 parent 7ec23d9 commit b16654c
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 11 deletions.
210 changes: 210 additions & 0 deletions src/pages/TotalStatisticsPage/GraphChart.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import React, { useContext } from 'react';
import { Bar } from 'react-chartjs-2';
import { SortedStudent } from './TotalStatisticsPage';
import {
Chart as ChartJS,
BarElement,
Title,
Tooltip,
Legend as ChartLegend,
CategoryScale,
LinearScale,
} from 'chart.js';
import { GraphBox } from '@/components';
import styled from 'styled-components';
import { BsThreeDots } from 'react-icons/bs';

ChartJS.register(
BarElement,
Title,
Tooltip,
ChartLegend,
CategoryScale,
LinearScale,
);

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>
{topStudents.map((student, index) => (
<LegendItem key={student.studentName}>
<LegendCircle color={ratedColors[index]} />
<LegendText>{student.studentName}</LegendText>
<LegendDots />
<LegendPercentage>{`(${student.attendanceRate}%)`}</LegendPercentage>
</LegendItem>
))}
</LegendContainer>
);

const GraphChart = () => {
const studentData = useContext(SortedStudent);

if (!studentData || studentData.length === 0) {
return <div>Loading...</div>;
}

// 데이터를 직접 계산
const sortedStudents = [...studentData].sort(
(a, b) => b.attendanceRate - a.attendanceRate,
);
const topStudents = sortedStudents.slice(0, 5);

const labels = topStudents.map((student) => student.studentName);
const attendanceRates = topStudents.map((student) => student.attendanceRate);

const ratedColors = ['#1E88E5', '#42A5F5', '#90CAF9', '#BBDEFB', '#E3F2FD'];

const graphData = {
labels,
datasets: [
{
label: '출석률',
data: attendanceRates,
backgroundColor: ratedColors,
borderWidth: 1,
datalabels: {
color: '#fff',
align: 'end',
anchor: 'end',
},
},
],
};

const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
},
tooltip: {
backgroundColor: '#fff',
titleColor: '#000',
bodyColor: '#000',
borderColor: '#ccc',
borderWidth: 1,
callbacks: {
label: (context) => {
const label = context.label || '';
const value = context.raw || 0;
return `${label}: ${value}%`;
},
},
},
datalabels: {
display: true,
color: 'white',
font: {
weight: 'bold',
size: 14,
},
},
},
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
stepSize: 20,
font: {
size: 12,
},
},
title: {
display: false,
},
},
x: {
grid: {
display: true,
},
ticks: {
font: {
size: 12,
},
},
},
},
};

return (
<GraphBox title="출석률 좋은 학생 TOP 5">
<ChartContent>
<ChartArea>
<Bar data={graphData} options={options} />
</ChartArea>
<HiddenLegend>
<Legend topStudents={topStudents} ratedColors={ratedColors} />
</HiddenLegend>
</ChartContent>
</GraphBox>
);
};

export default GraphChart;
12 changes: 12 additions & 0 deletions src/pages/TotalStatisticsPage/TableChart.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { AttendeeTable } from '@/components';
import React from 'react';

const TableChart = () => {
return (
<div>
<AttendeeTable />
</div>
);
};

export default TableChart;
34 changes: 24 additions & 10 deletions src/pages/TotalStatisticsPage/TotalStatisticsPage.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import * as S from './TotalStatisticsPage.style';
import { PageLayout } from '@/Layout';
import { axiosInstance } from '@/axios';
import { Dropdown, TopNavigation } from '@/components';
import { useEffect, useState } from 'react';
import { DaterangePicker, Dropdown, TopNavigation } from '@/components';
import { createContext, useEffect, useState } from 'react';
import GraphChart from './GraphChart';
import TableChart from './TableChart';

export const SortedStudent = createContext();

const TotalStatisticsPage = () => {
const USER_ID = sessionStorage.getItem('id');
const [viewMode, setViewMode] = useState('그래프');
const [studentGraph, setStudentGraph] = useState([]);

useEffect(() => {
const fetchStatistics = async () => {
try {
const statisticsResponse = await axiosInstance.get(
`/api/v1/events/statistic/student/100?memberId=${USER_ID}&authority=MEMBER&member=true`,
`/api/v1/events/statistic/student`,
);

console.log(statisticsResponse);
const sortedData = statisticsResponse.data.sort(
(a, b) => b.attendanceRate - a.attendanceRate,
);
setStudentGraph(sortedData);
} catch (error) {
console.error('Error fetching statistics:', error);
}
Expand All @@ -28,11 +35,18 @@ const TotalStatisticsPage = () => {
<PageLayout topNavigation={<TopNavigation />}>
<S.Container>
<S.TotalStatisticsPage>
<Dropdown
items={['그래프', '표']}
defaultItem={'그래프'}
onSelect={setViewMode}
/>
<S.FlexBox>
<Dropdown
items={['그래프', '표']}
defaultItem={'그래프'}
onSelect={setViewMode}
/>
<DaterangePicker />
</S.FlexBox>
<SortedStudent.Provider value={studentGraph}>
{viewMode === '그래프' && <GraphChart />}
{viewMode === '표' && <TableChart />}
</SortedStudent.Provider>
</S.TotalStatisticsPage>
</S.Container>
</PageLayout>
Expand Down
6 changes: 5 additions & 1 deletion src/pages/TotalStatisticsPage/TotalStatisticsPage.style.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export const TotalStatisticsPage = styled.div`
margin: 0 auto;
padding: 50px 20px;
gap: 30px;
border: 1px solid red;
`;

border: 1px solid red; /* 임시 코드 */
export const FlexBox = styled.div`
display: flex;
justify-content: space-between;
`;

0 comments on commit b16654c

Please sign in to comment.