Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ 통계 페이지 개선 #32

Merged
merged 4 commits into from
Feb 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/api/statistics/category/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NextResponse } from "next/server";

export async function GET() {
const response = await fetch(
"http://34.64.250.183:8080/api/statistics/category"
);
const data = await response.json();

return NextResponse.json(data);
}
10 changes: 10 additions & 0 deletions app/api/statistics/summary/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NextResponse } from "next/server";

export async function GET() {
const response = await fetch(
"http://34.64.250.183:8080/api/statistics/summary"
);
const data = await response.json();

return NextResponse.json(data);
}
10 changes: 10 additions & 0 deletions app/api/statistics/weekly/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NextResponse } from "next/server";

export async function GET() {
const response = await fetch(
"http://34.64.250.183:8080/api/statistics/weekly"
);
const data = await response.json();

return NextResponse.json(data);
}
212 changes: 124 additions & 88 deletions app/statistics/page.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { Container, Typography } from "@mui/material";
import { Responsive, WidthProvider } from "react-grid-layout";
import "@/styles/react-grid.css";
Expand All @@ -18,11 +18,6 @@ import {
RadialLinearScale,
} from "chart.js";
import ChartDataLabels from "chartjs-plugin-datalabels";
import {
mockSummaryStatisticsData,
mockWeeklyStatisticsData,
mockCategoryStatisticsData,
} from "@/constants/mockStatisticsResponseData";

import TotalSolvedChart from "@/components/statistics/summary/total-solved";
import CorrectRateChart from "@/components/statistics/summary/correct-rate";
Expand All @@ -31,7 +26,7 @@ import CategorySolvedChart from "@/components/statistics/category/category-solve
import CategoryAccuracyChart from "@/components/statistics/category/category-accuracy";
import WeeklyTrendChart from "@/components/statistics/weekly/weekly-trend";
import { statisticUtils } from "@/utils/statisticUtils";
import {Bell, Settings} from "lucide-react";
import { Bell, Settings } from "lucide-react";

const ResponsiveGridLayout = WidthProvider(Responsive);

Expand Down Expand Up @@ -69,6 +64,48 @@ export default function StatisticsPage() {
],
});

const [summaryData, setSummaryData] = useState(null);
const [weeklyData, setWeeklyData] = useState(null);
const [categoryData, setCategoryData] = useState(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
const fetchData = async () => {
try {
// API 호출
const [summaryRes, weeklyRes, categoryRes] = await Promise.all([
fetch("/api/statistics/summary"),
fetch("/api/statistics/weekly"),
fetch("/api/statistics/category"),
]);

// JSON 파싱
const summary = await summaryRes.json();
const weekly = await weeklyRes.json();
const category = await categoryRes.json();

// 상태 업데이트
setSummaryData(summary);
setWeeklyData(weekly);
setCategoryData(category);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setIsLoading(false);
}
};

fetchData();
}, []);

if (isLoading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div>Loading...</div>
</div>
);
}

const gridItemStyle = {
backgroundColor: "#fff",
borderRadius: "8px",
Expand All @@ -78,92 +115,91 @@ export default function StatisticsPage() {
};

return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<header className="bg-[#6DB1B2] text-white py-6">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold">나의 학습통계</h1>
<div className="flex gap-4">
<Bell className="w-6 h-6 cursor-pointer hover:opacity-80"/>
<Settings className="w-6 h-6 cursor-pointer hover:opacity-80"/>
</div>
<div className="min-h-screen bg-gray-50">
{/* Header */}
<header className="bg-[#6DB1B2] text-white py-6">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold">나의 학습통계</h1>
<div className="flex gap-4">
<Bell className="w-6 h-6 cursor-pointer hover:opacity-80" />
<Settings className="w-6 h-6 cursor-pointer hover:opacity-80" />
</div>
</div>
</header>

<Container maxWidth={false} disableGutters sx={{p: 3, height: "100vh"}}>
{/*<Typography variant="h4" gutterBottom sx={{mb: 4}}>*/}
{/* 나의 학습 통계*/}
{/*</Typography>*/}

<ResponsiveGridLayout
className="layout"
layouts={layouts}
breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}}
cols={{lg: 12, md: 12, sm: 6, xs: 4, xxs: 2}}
rowHeight={50}
margin={[16, 16]}
compactType="vertical"
preventCollision={false}
isResizable={true}
isDraggable={true}
onLayoutChange={(currentLayout, allLayouts) => {
setLayouts(allLayouts);
}}
>
<div key="category" style={gridItemStyle}>
<CategorySolvedChart data={mockCategoryStatisticsData}/>
</div>
</div>
</header>

<div key="weekly" style={gridItemStyle}>
<WeeklyTrendChart data={mockWeeklyStatisticsData}/>
</div>
{/* 나의 학습 통계 */}
<Container maxWidth={false} disableGutters sx={{ p: 3, height: "100vh" }}>
<ResponsiveGridLayout
className="layout"
layouts={layouts}
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 12, sm: 6, xs: 4, xxs: 2 }}
rowHeight={50}
margin={[16, 16]}
compactType="vertical"
preventCollision={false}
isResizable={true}
isDraggable={true}
onLayoutChange={(currentLayout, allLayouts) => {
setLayouts(allLayouts);
}}
>
<div key="category" style={gridItemStyle}>
<CategorySolvedChart data={categoryData} />
</div>

<div key="summary-solved" style={gridItemStyle}>
<Typography variant="h6" color="primary" gutterBottom>
총 해결한 문제
</Typography>
<Typography variant="h3">
{mockSummaryStatisticsData.data.user.totalSolved}문제
</Typography>
</div>
<div key="summary-accuracy" style={gridItemStyle}>
<Typography variant="h6" color="primary" gutterBottom>
나의 정답률
</Typography>
<Typography variant="h3">
{mockSummaryStatisticsData.data.user.correctRate}%
</Typography>
</div>
<div key="summary-time" style={gridItemStyle}>
<Typography variant="h6" color="primary" gutterBottom>
평균 풀이 시간
</Typography>
<Typography variant="h3">
{statisticUtils.formatters.timeInSeconds(
mockSummaryStatisticsData.data.user.timeStats.averageTime
)}
</Typography>
</div>
<div key="weekly" style={gridItemStyle}>
<WeeklyTrendChart data={weeklyData} />
</div>

<div key="problem-solved" style={gridItemStyle}>
<TotalSolvedChart data={mockSummaryStatisticsData}/>
</div>
<div key="summary-solved" style={gridItemStyle}>
<Typography variant="h6" color="primary" gutterBottom>
총 해결한 문제
</Typography>
<Typography variant="h3">
{summaryData.data.user.totalSolved}문제
</Typography>
</div>

<div key="correct-rate" style={gridItemStyle}>
<CorrectRateChart data={mockSummaryStatisticsData}/>
</div>
<div key="summary-accuracy" style={gridItemStyle}>
<Typography variant="h6" color="primary" gutterBottom>
나의 정답률
</Typography>
<Typography variant="h3">
{summaryData.data.user.correctRate}%
</Typography>
</div>

<div key="distribution" style={gridItemStyle}>
<CorrectDistributionChart data={mockSummaryStatisticsData}/>
</div>
<div key="summary-time" style={gridItemStyle}>
<Typography variant="h6" color="primary" gutterBottom>
평균 풀이 시간
</Typography>
<Typography variant="h3">
{statisticUtils.formatters.timeInSeconds(
summaryData.data.user.timeStats.averageTime
)}
</Typography>
</div>

<div key="category-accuracy" style={gridItemStyle}>
<CategoryAccuracyChart data={mockCategoryStatisticsData}/>
</div>
</ResponsiveGridLayout>
</Container>
</div>
);
}
<div key="problem-solved" style={gridItemStyle}>
<TotalSolvedChart data={summaryData} />
</div>

<div key="correct-rate" style={gridItemStyle}>
<CorrectRateChart data={summaryData} />
</div>

<div key="distribution" style={gridItemStyle}>
<CorrectDistributionChart data={summaryData} />
</div>

<div key="category-accuracy" style={gridItemStyle}>
<CategoryAccuracyChart data={categoryData} />
</div>
</ResponsiveGridLayout>
</Container>
</div>
);
}
Loading