Skip to content

Commit 2264f36

Browse files
committed
feat: 성적 페이지 페이지네이션 적용
1 parent e7d9518 commit 2264f36

File tree

4 files changed

+93
-26
lines changed

4 files changed

+93
-26
lines changed

src/components/scores/gpa/GpaScoreTable.tsx

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
TableHeader,
1414
TableRow,
1515
} from "@/components/ui/table";
16+
import { Button } from "@/components/ui/button";
1617

1718
interface Props {
1819
verifyFilter: VerifyStatus;
@@ -22,7 +23,8 @@ const S3_BASE_URL = import.meta.env.VITE_S3_BASE_URL;
2223

2324
export function GpaScoreTable({ verifyFilter }: Props) {
2425
const [scores, setScores] = useState<GpaScoreWithUser[]>([]);
25-
const [page] = useState(1);
26+
const [page, setPage] = useState(1);
27+
const [totalPages, setTotalPages] = useState(1);
2628
const [loading, setLoading] = useState(false);
2729
const [editingId, setEditingId] = useState<number | null>(null);
2830
const [editingGpa, setEditingGpa] = useState<number>(0);
@@ -36,6 +38,7 @@ export function GpaScoreTable({ verifyFilter }: Props) {
3638
page,
3739
);
3840
setScores(response.content);
41+
setTotalPages(response.totalPages);
3942
} catch (error) {
4043
console.error("Failed to fetch GPA scores:", error);
4144
} finally {
@@ -95,6 +98,11 @@ export function GpaScoreTable({ verifyFilter }: Props) {
9598
}
9699
};
97100

101+
const handlePageChange = (newPage: number) => {
102+
if (newPage < 1 || newPage > totalPages) return;
103+
setPage(newPage);
104+
};
105+
98106
return (
99107
<div className="rounded-lg border bg-white shadow">
100108
<div className="overflow-x-auto">
@@ -174,28 +182,28 @@ export function GpaScoreTable({ verifyFilter }: Props) {
174182
}
175183
className="w-20 rounded border px-2 py-1"
176184
/>
177-
<button
185+
<Button
178186
onClick={() => handleSave(score)}
179-
className="rounded bg-blue-500 px-2 py-1 text-white hover:bg-blue-600"
187+
variant="default"
180188
>
181189
저장
182-
</button>
183-
<button
190+
</Button>
191+
<Button
184192
onClick={() => setEditingId(null)}
185-
className="rounded bg-gray-500 px-2 py-1 text-white hover:bg-gray-600"
193+
variant="secondary"
186194
>
187195
취소
188-
</button>
196+
</Button>
189197
</div>
190198
) : (
191199
<div className="flex gap-2">
192200
{score.gpaScoreStatusResponse.gpaResponse.gpaCriteria}
193-
<button
201+
<Button
194202
onClick={() => handleEdit(score)}
195-
className="rounded bg-gray-100 px-2 py-1 text-gray-600 hover:bg-gray-200"
203+
variant="secondary"
196204
>
197205
수정
198-
</button>
206+
</Button>
199207
</div>
200208
)}
201209
</TableCell>
@@ -241,7 +249,32 @@ export function GpaScoreTable({ verifyFilter }: Props) {
241249
</TableBody>
242250
</Table>
243251
</div>
244-
{/* 페이지네이션 추가 예정 */}
252+
{/* 페이지네이션 추가 */}
253+
<div className="mt-4 flex items-center justify-center gap-2">
254+
<Button
255+
onClick={() => handlePageChange(page - 1)}
256+
disabled={page === 1}
257+
variant="secondary"
258+
>
259+
이전
260+
</Button>
261+
{Array.from({ length: totalPages }, (_, idx) => (
262+
<Button
263+
key={idx + 1}
264+
onClick={() => handlePageChange(idx + 1)}
265+
variant={page === idx + 1 ? "default" : "secondary"}
266+
>
267+
{idx + 1}
268+
</Button>
269+
))}
270+
<Button
271+
onClick={() => handlePageChange(page + 1)}
272+
disabled={page === totalPages}
273+
variant="secondary"
274+
>
275+
다음
276+
</Button>
277+
</div>
245278
</div>
246279
);
247280
}

src/components/scores/language/LanguageScoreTable.tsx

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
TableHeader,
1818
TableRow,
1919
} from "@/components/ui/table";
20+
import { Button } from "@/components/ui/button";
2021

2122
interface Props {
2223
verifyFilter: VerifyStatus;
@@ -41,7 +42,8 @@ const LANGUAGE_TEST_OPTIONS: { value: LanguageTestType; label: string }[] = [
4142

4243
export function LanguageScoreTable({ verifyFilter }: Props) {
4344
const [scores, setScores] = useState<LanguageScoreWithUser[]>([]);
44-
const [page] = useState(1);
45+
const [page, setPage] = useState(1);
46+
const [totalPages, setTotalPages] = useState(1);
4547
const [loading, setLoading] = useState(false);
4648
const [editingId, setEditingId] = useState<number | null>(null);
4749
const [editingScore, setEditingScore] = useState<string>("");
@@ -55,6 +57,7 @@ export function LanguageScoreTable({ verifyFilter }: Props) {
5557
page,
5658
);
5759
setScores(response.content);
60+
setTotalPages(response.totalPages);
5861
} catch (error) {
5962
console.error("Failed to fetch Language scores:", error);
6063
} finally {
@@ -122,6 +125,11 @@ export function LanguageScoreTable({ verifyFilter }: Props) {
122125
}
123126
};
124127

128+
const handlePageChange = (newPage: number) => {
129+
if (newPage < 1 || newPage > totalPages) return;
130+
setPage(newPage);
131+
};
132+
125133
return (
126134
<div className="rounded-lg border bg-white shadow">
127135
<div className="overflow-x-auto">
@@ -211,31 +219,31 @@ export function LanguageScoreTable({ verifyFilter }: Props) {
211219
onChange={(e) => setEditingScore(e.target.value)}
212220
className="w-20 rounded border px-2 py-1"
213221
/>
214-
<button
222+
<Button
215223
onClick={() => handleSave(score)}
216-
className="rounded bg-blue-500 px-2 py-1 text-white hover:bg-blue-600"
224+
variant="default"
217225
>
218226
저장
219-
</button>
220-
<button
227+
</Button>
228+
<Button
221229
onClick={() => setEditingId(null)}
222-
className="rounded bg-gray-500 px-2 py-1 text-white hover:bg-gray-600"
230+
variant="secondary"
223231
>
224232
취소
225-
</button>
233+
</Button>
226234
</div>
227235
) : (
228236
<div className="flex gap-2">
229237
{
230238
score.languageTestScoreStatusResponse
231239
.languageTestResponse.languageTestScore
232240
}
233-
<button
241+
<Button
234242
onClick={() => handleEdit(score)}
235-
className="rounded bg-gray-100 px-2 py-1 text-gray-600 hover:bg-gray-200"
243+
variant="secondary"
236244
>
237245
수정
238-
</button>
246+
</Button>
239247
</div>
240248
)}
241249
</TableCell>
@@ -286,6 +294,31 @@ export function LanguageScoreTable({ verifyFilter }: Props) {
286294
</TableBody>
287295
</Table>
288296
</div>
297+
<div className="mt-4 flex items-center justify-center gap-2">
298+
<Button
299+
onClick={() => handlePageChange(page - 1)}
300+
disabled={page === 1}
301+
variant="secondary"
302+
>
303+
이전
304+
</Button>
305+
{Array.from({ length: totalPages }, (_, idx) => (
306+
<Button
307+
key={idx + 1}
308+
onClick={() => handlePageChange(idx + 1)}
309+
variant={page === idx + 1 ? "default" : "secondary"}
310+
>
311+
{idx + 1}
312+
</Button>
313+
))}
314+
<Button
315+
onClick={() => handlePageChange(page + 1)}
316+
disabled={page === totalPages}
317+
variant="secondary"
318+
>
319+
다음
320+
</Button>
321+
</div>
289322
</div>
290323
);
291324
}

src/lib/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { clsx, type ClassValue } from "clsx"
2-
import { twMerge } from "tailwind-merge"
1+
import { clsx, type ClassValue } from "clsx";
2+
import { twMerge } from "tailwind-merge";
33

44
export function cn(...inputs: ClassValue[]) {
5-
return twMerge(clsx(inputs))
5+
return twMerge(clsx(inputs));
66
}

src/routes/scores/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { useState } from "react";
2-
import { GpaScoreTable } from "@/components/scores/gpa/GpaScoreTable"; // 확장자 제거
3-
import { LanguageScoreTable } from "@/components/scores/language/LanguageScoreTable"; // 확장자 제거
2+
import { GpaScoreTable } from "@/components/scores/gpa/GpaScoreTable";
3+
import { LanguageScoreTable } from "@/components/scores/language/LanguageScoreTable";
44
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
55
import { VerifyStatus } from "@/types/scores";
6+
67
export default function ScoresPage() {
78
const [verifyFilter, setVerifyFilter] = useState<VerifyStatus>("PENDING");
89

0 commit comments

Comments
 (0)