Skip to content

Commit

Permalink
Merge pull request #94 from su-its/feat/ranking
Browse files Browse the repository at this point in the history
Feat/ranking
  • Loading branch information
KinjiKawaguchi authored Apr 6, 2024
2 parents 12ff5fa + 0d2323c commit b3be100
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 182 deletions.
2 changes: 1 addition & 1 deletion typing-app/.env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
API_URL=http://localhost:8080
NEXT_PUBLIC_API_URL=http://localhost:8080
6 changes: 3 additions & 3 deletions typing-app/src/components/atoms/RefreshButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ const RefreshIcon = () => (
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<polyline points="23 4 23 10 17 10"></polyline>
<polyline points="1 20 1 14 7 14"></polyline>
Expand Down
6 changes: 3 additions & 3 deletions typing-app/src/components/molecules/RankingTableBody.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Tbody } from "@chakra-ui/react";
import RankingTableRow from "./RankingTableRow";
import { ScoreRanking } from "../organism/RankingTabs";
import { components } from "@/libs/api/v1";

export type RankingTableBodyProps = {
scoreRankings: ScoreRanking[];
scoreRankings: components["schemas"]["ScoreRanking"][];
};

const RankingTableBody: React.FC<RankingTableBodyProps> = ({ scoreRankings }) => {
return (
<Tbody>
{scoreRankings.map((scoreRanking) => (
<RankingTableRow key={String(scoreRanking.rank)} {...scoreRanking} />
<RankingTableRow key={String(scoreRanking.score?.user?.student_number)} {...scoreRanking} />
))}
</Tbody>
);
Expand Down
26 changes: 19 additions & 7 deletions typing-app/src/components/molecules/RankingTableRow.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import { Td, Tr } from "@chakra-ui/react";
import { ScoreRanking } from "../organism/RankingTabs";
import { components } from "@/libs/api/v1";

const RankingTableRow: React.FC<ScoreRanking> = (scoreRanking) => {
const RankingTableRow: React.FC<components["schemas"]["ScoreRanking"]> = (scoreRanking) => {
const accuracy = scoreRanking.score?.accuracy ?? 0;
const formattedAccuracy = new Intl.NumberFormat("en-US", { style: "percent", maximumFractionDigits: 2 }).format(
accuracy
);
const formattedCreatedAt = scoreRanking.score?.created_at
? new Date(scoreRanking.score.created_at).toISOString().split("T")[0]
: "";
return (
<Tr key={String(scoreRanking.rank)} _even={{ bg: "midnightblue" }} _odd={{ bg: "#192f70" }} color={"silver"}>
<Tr
key={String(scoreRanking.score?.user?.student_number)}
_even={{ bg: "midnightblue" }}
_odd={{ bg: "#192f70" }}
color={"silver"}
>
<Td width={"128px"} textAlign={"center"}>
{String(scoreRanking.rank)}
</Td>
<Td width={"256px"} textAlign={"center"}>
{scoreRanking.user.studentNumber}
{scoreRanking.score?.user?.student_number}
</Td>
<Td width={"320px"} textAlign={"center"}>
{String(scoreRanking.keystrokes)}
{String(scoreRanking.score?.keystrokes)}
</Td>
<Td width={"256px"} textAlign={"center"}>
{String(scoreRanking.accuracy)}
{formattedAccuracy}
</Td>
<Td width={"320px"} textAlign={"center"}>
{scoreRanking.createdAt.toLocaleDateString("ja-JP")}
{formattedCreatedAt}
</Td>
</Tr>
);
Expand Down
216 changes: 49 additions & 167 deletions typing-app/src/components/organism/RankingTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,56 @@
"use client";

import { Tabs, TabList, TabPanels, Tab, TabPanel, Flex, Center, Box, Grid } from "@chakra-ui/react";
import RankingTable from "../organism/RankingTable";
import { Pagination } from "../molecules/Pagination";
//import { CustomButton } from "../atoms/CustomButton";
import RefreshButton from "../atoms/RefreshButton";
import { useEffect, useState } from "react";
// import { client } from "@/libs/api";
import { client } from "@/libs/api";
import { components } from "@/libs/api/v1";

const RankingTabs = () => {
const [scoreRankings, setScoreRankings] = useState<ScoreRanking[]>(demoAccuracyRankings);
const [rankingStartFrom, setRankingStartFrom] = useState(0);
const [scoreRankings, setScoreRankings] = useState<components["schemas"]["ScoreRanking"][]>([]);
const [rankingStartFrom, setRankingStartFrom] = useState(1);
const [sortBy, setSortBy] = useState<"accuracy" | "keystrokes">("accuracy");
const LIMIT = 10;
const [error, setError] = useState<string | undefined>(undefined);
const [totalRankingCount, setTotalRankingCount] = useState<number>(0);

const MAXIMUM = 100; // TODO: MAXIMUMをAPIから取得する
const LIMIT = 10;
const MAXIMUM = totalRankingCount;

const fetchData = async () => {
const { data } = await client.GET("/scores/ranking", {
params: {
query: {
sort_by: sortBy,
start: rankingStartFrom,
limit: LIMIT,
},
},
});
if (data) {
setScoreRankings(data.rankings);
setTotalRankingCount(data.total_count);
} else {
setError("データの取得中にエラーが発生しました。");
}
};
useEffect(() => {
// ページが読み込まれたときにデータを取得
fetchData();
});
}, [sortBy, rankingStartFrom]);

const handleTabChange = (index: number) => {
if (index === 0) {
setSortBy("accuracy");
} else if (index === 1) {
setSortBy("keystrokes");
}

fetchData;
const sortOption = index === 0 ? "accuracy" : "keystrokes";
setSortBy(sortOption);
setRankingStartFrom(1);
};

// 演算子を引数にとる、ボタンを押したときのハンドラ関数
const handlePaginationClick = (direction: "next" | "prev") => {
const newStartFrom =
direction === "prev" ? Math.max(rankingStartFrom - LIMIT, 0) : Math.min(rankingStartFrom + LIMIT, MAXIMUM);
direction === "prev"
? Math.max(rankingStartFrom - LIMIT, 1)
: Math.min(rankingStartFrom + LIMIT, MAXIMUM - LIMIT);
setRankingStartFrom(newStartFrom);

fetchData;
};

const fetchData = async () => {
// TODO: APIを使ってデータをフェッチ
if (sortBy == "accuracy") {
setScoreRankings(demoAccuracyRankings);
} else if (sortBy == "keystrokes") {
setScoreRankings(demoKeyStrokeRankings);
}
};

return (
Expand All @@ -57,10 +62,21 @@ const RankingTabs = () => {
<Tab _selected={{ color: "#00ace6" }}>正打率</Tab>
<Tab _selected={{ color: "#00ace6" }}>入力文字数</Tab>
</TabList>
<RefreshButton onClick={() => fetchData()} isDisabled={false} />
<RefreshButton
onClick={() => {
setRankingStartFrom(1);
fetchData();
}}
isDisabled={false}
/>
</Grid>
</Flex>

{error && (
<Center>
<Box>Error: {error}</Box>
</Center>
)}
(
<TabPanels>
<TabPanel>
<RankingTable scoreRankings={scoreRankings} />
Expand All @@ -69,151 +85,17 @@ const RankingTabs = () => {
<RankingTable scoreRankings={scoreRankings} />
</TabPanel>
</TabPanels>
)
<Center>
<Pagination
onPrev={() => handlePaginationClick("prev")}
onNext={() => handlePaginationClick("next")}
isPrevDisabled={rankingStartFrom <= 0}
isPrevDisabled={rankingStartFrom <= 1}
isNextDisabled={rankingStartFrom + LIMIT >= MAXIMUM}
/>
</Center>
</Tabs>
);
};
export default RankingTabs;

export interface User {
id: string;
studentNumber: string;
handleName: string;
}

export interface ScoreRanking {
rank: Number;
user: User;
keystrokes: Number;
accuracy: Number;
createdAt: Date;
}

const demoUsers: User[] = [
{
id: "1",
studentNumber: "70310000",
handleName: "X",
},
{
id: "2",
studentNumber: "70310000",
handleName: "Y",
},
{
id: "3",
studentNumber: "70310000",
handleName: "Z",
},
{
id: "4",
studentNumber: "70310000",
handleName: "A",
},
{
id: "5",
studentNumber: "70310000",
handleName: "B",
},
];

const demoKeyStrokeRankings: ScoreRanking[] = [
{
rank: 1,
user: {
id: "1",
studentNumber: "70310000",
handleName: "X",
},
keystrokes: 100,
accuracy: 100,
createdAt: new Date(),
},
{
rank: 2,
user: {
id: "2",
studentNumber: "70310000",
handleName: "Y",
},
keystrokes: 90,
accuracy: 90,
createdAt: new Date(),
},
{
rank: 3,
user: {
id: "3",
studentNumber: "70310000",
handleName: "Z",
},
keystrokes: 80,
accuracy: 80,
createdAt: new Date(),
},
];

const demoAccuracyRankings: ScoreRanking[] = [
{
rank: 1,
user: {
id: "1",
studentNumber: "70310000",
handleName: "X",
},
keystrokes: 100,
accuracy: 100,
createdAt: new Date(),
},
{
rank: 2,
user: {
id: "2",
studentNumber: "70310000",
handleName: "Y",
},
keystrokes: 90,
accuracy: 90,
createdAt: new Date(),
},
{
rank: 3,
user: {
id: "3",
studentNumber: "70310000",
handleName: "Z",
},
keystrokes: 80,
accuracy: 80,
createdAt: new Date(),
},
{
rank: 4,
user: {
id: "4",
studentNumber: "70310000",
handleName: "A",
},
keystrokes: 70,
accuracy: 70,
createdAt: new Date(),
},
{
rank: 5,
user: {
id: "5",
studentNumber: "70310000",
handleName: "B",
},
keystrokes: 60,
accuracy: 60,
createdAt: new Date(),
},
];
export default RankingTabs;
2 changes: 1 addition & 1 deletion typing-app/src/libs/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import createClient from "openapi-fetch";
import { paths } from "./v1";

export const client = createClient<paths>({ baseUrl: process.env.API_URL });
export const client = createClient<paths>({ baseUrl: process.env.NEXT_PUBLIC_API_URL });

0 comments on commit b3be100

Please sign in to comment.