Skip to content
Merged
Changes from 2 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
163 changes: 137 additions & 26 deletions apps/admin/src/routes/scores/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { createFileRoute, redirect } from "@tanstack/react-router";
import { Building2, FileText, Search, SquarePen, UserCircle2 } from "lucide-react";
import { useState } from "react";
import { GpaScoreTable } from "@/components/features/scores/GpaScoreTable";
import { LanguageScoreTable } from "@/components/features/scores/LanguageScoreTable";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { cn } from "@/lib/utils";
import { isTokenExpired } from "@/lib/utils/jwtUtils";
import { loadAccessToken } from "@/lib/utils/localStorage";
import type { VerifyStatus } from "@/types/scores";
Expand All @@ -22,37 +26,144 @@ export const Route = createFileRoute("/scores/")({

function ScoresPage() {
const [verifyFilter, setVerifyFilter] = useState<VerifyStatus>("PENDING");
const [searchKeyword, setSearchKeyword] = useState("");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐ŸŸก Minor

2. searchKeyword ์ƒํƒœ๊ฐ€ ์ˆ˜์ง‘๋งŒ ๋˜๊ณ  ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

searchKeyword๋Š” Input์— ๋ฐ”์ธ๋”ฉ๋˜์–ด ์ž…๋ ฅ๊ฐ’์„ ์ถ”์ ํ•˜์ง€๋งŒ, GpaScoreTable์ด๋‚˜ LanguageScoreTable์— ์ „๋‹ฌ๋˜์ง€ ์•Š๊ณ , ํ•„ํ„ฐ๋ง์ด๋‚˜ API ํ˜ธ์ถœ์—๋„ ์“ฐ์ด์ง€ ์•Š์•„ ํ˜„์žฌ๋กœ์„œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒ€์ƒ‰ UI์ž…๋‹ˆ๋‹ค. ์ถ”ํ›„ ์—ฐ๋™ ์˜ˆ์ •์ด๋ผ๋ฉด ๊ดœ์ฐฎ์ง€๋งŒ, ์˜๋„๋ฅผ ๋ช…ํ™•ํžˆ ๋‚จ๊ฒจ๋‘์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

Also applies to: 97-105

๐Ÿค– Prompt for AI Agents
In `@apps/admin/src/routes/scores/index.tsx` at line 29, The searchKeyword state
(searchKeyword, setSearchKeyword) is only bound to the Input but never used by
GpaScoreTable or LanguageScoreTable (also referenced around the block for lines
97-105), leaving the search UI non-functional; either remove the unused state
and Input or wire it through by passing searchKeyword as a prop to GpaScoreTable
and LanguageScoreTable (e.g., add a prop like searchKeyword) and update those
components to filter their data or trigger the appropriate API call when the
value changesโ€”alternatively, add a clear TODO comment near the Input and state
to indicate intentional future hookup.


const sideMenus = [
{ label: "๋Œ€ํ•™ ๊ด€๋ฆฌ", icon: Building2, active: true },
{ label: "๋ฉ˜ํ†  ๊ด€๋ฆฌ", icon: UserCircle2, active: false },
{ label: "์œ ์ € ๊ด€๋ฆฌ", icon: UserCircle2, active: false },
{ label: "์„ฑ์  ๊ด€๋ฆฌ", icon: FileText, active: false },
] as const;
Comment on lines +31 to +36
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐ŸŸก Minor

1. ์‚ฌ์ด๋“œ๋ฐ” ํ™œ์„ฑ ๋ฉ”๋‰ด ํ•ญ๋ชฉ์ด ํ˜„์žฌ ํŽ˜์ด์ง€์™€ ๋ถˆ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.

์ด ํŒŒ์ผ์€ /scores ๋ผ์šฐํŠธ(์„ฑ์  ๊ด€๋ฆฌ ํŽ˜์ด์ง€)์ธ๋ฐ, sideMenus์—์„œ active: true๋กœ ์„ค์ •๋œ ํ•ญ๋ชฉ์€ "๋Œ€ํ•™ ๊ด€๋ฆฌ"์ž…๋‹ˆ๋‹ค. "์„ฑ์  ๊ด€๋ฆฌ"๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด์•ผ ํ•  ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

๐Ÿ› ์ˆ˜์ • ์ œ์•ˆ
 const sideMenus = [
-    { label: "๋Œ€ํ•™ ๊ด€๋ฆฌ", icon: Building2, active: true },
+    { label: "๋Œ€ํ•™ ๊ด€๋ฆฌ", icon: Building2, active: false },
     { label: "๋ฉ˜ํ†  ๊ด€๋ฆฌ", icon: UserCircle2, active: false },
     { label: "์œ ์ € ๊ด€๋ฆฌ", icon: UserCircle2, active: false },
-    { label: "์„ฑ์  ๊ด€๋ฆฌ", icon: FileText, active: false },
+    { label: "์„ฑ์  ๊ด€๋ฆฌ", icon: FileText, active: true },
 ] as const;
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sideMenus = [
{ label: "๋Œ€ํ•™ ๊ด€๋ฆฌ", icon: Building2, active: true },
{ label: "๋ฉ˜ํ†  ๊ด€๋ฆฌ", icon: UserCircle2, active: false },
{ label: "์œ ์ € ๊ด€๋ฆฌ", icon: UserCircle2, active: false },
{ label: "์„ฑ์  ๊ด€๋ฆฌ", icon: FileText, active: false },
] as const;
const sideMenus = [
{ label: "๋Œ€ํ•™ ๊ด€๋ฆฌ", icon: Building2, active: false },
{ label: "๋ฉ˜ํ†  ๊ด€๋ฆฌ", icon: UserCircle2, active: false },
{ label: "์œ ์ € ๊ด€๋ฆฌ", icon: UserCircle2, active: false },
{ label: "์„ฑ์  ๊ด€๋ฆฌ", icon: FileText, active: true },
] as const;
๐Ÿค– Prompt for AI Agents
In `@apps/admin/src/routes/scores/index.tsx` around lines 31 - 36, The sideMenus
array currently marks the wrong menu as active; update the sideMenus constant so
the item with label "์„ฑ์  ๊ด€๋ฆฌ" has active: true and all other items (e.g., the item
with label "๋Œ€ํ•™ ๊ด€๋ฆฌ") have active: false. Locate the sideMenus declaration in this
file and flip the active flag accordingly (ensure the array remains typed as
const and the icons/labels are unchanged).


const topTabs = ["๊ถŒ์—ญ/๋‚˜๋ผ", "๋‚˜๋ผ/๋Œ€ํ•™", "๋Œ€ํ•™ ์ง€์› ์ •๋ณด", "๋Œ€ํ•™ ์ƒ์„ธ ์ •๋ณด"] as const;

const verifyFilters: Array<{ value: VerifyStatus; label: string }> = [
{ value: "PENDING", label: "๋Œ€๊ธฐ์ค‘" },
{ value: "APPROVED", label: "์Šน์ธ๋จ" },
{ value: "REJECTED", label: "๊ฑฐ์ ˆ๋จ" },
];

return (
<div className="container mx-auto py-6">
<h1 className="mb-6 text-2xl font-bold">์„ฑ์  ๊ด€๋ฆฌ</h1>

<div className="mb-4">
<select
value={verifyFilter}
onChange={(e) => setVerifyFilter(e.target.value as VerifyStatus)}
className="rounded border p-2"
>
<option value="PENDING">๋Œ€๊ธฐ์ค‘</option>
<option value="APPROVED">์Šน์ธ๋จ</option>
<option value="REJECTED">๊ฑฐ์ ˆ๋จ</option>
</select>
</div>
<div className="mx-auto w-full max-w-[1280px] rounded-[24px] border border-slate-200 bg-white shadow-[0_16px_40px_-28px_rgba(15,23,42,0.45)]">
<div className="flex min-h-[calc(100vh-96px)]">
<aside className="flex w-[212px] flex-col border-r border-slate-100 bg-slate-50/70 px-5 py-7">
<div className="mb-10">
<p className="text-[11px] font-semibold text-indigo-400">Solid Connection</p>
<h1 className="mt-1 text-[34px] font-extrabold leading-[1.04] tracking-[-0.03em] text-indigo-500">
Admin
<br />
boards
</h1>
</div>

<p className="mb-4 text-xs font-semibold text-slate-400">๊ด€๋ฆฌ ๋ฉ”๋‰ด</p>
<nav className="space-y-1.5">
{sideMenus.map((menu) => (
<button
key={menu.label}
type="button"
className={cn(
"flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-left text-[13px] font-medium transition-colors",
menu.active ? "bg-indigo-50 text-indigo-600" : "text-slate-400 hover:bg-white hover:text-slate-600",
)}
>
<menu.icon className="h-4 w-4" />
{menu.label}
</button>
))}
</nav>
</aside>

<section className="flex-1 bg-slate-50/30 p-7">
<div className="h-full rounded-2xl border border-slate-100 bg-white p-6 shadow-[0_8px_24px_-22px_rgba(15,23,42,0.55)]">
<div className="inline-flex items-center gap-1.5 rounded-lg bg-slate-100 p-1">
{topTabs.map((tab) => (
<button
key={tab}
type="button"
className={cn(
"rounded-md px-3 py-1.5 text-xs font-semibold tracking-[-0.01em] transition-colors",
tab === "๊ถŒ์—ญ/๋‚˜๋ผ" ? "bg-white text-indigo-600 shadow-sm" : "text-slate-500 hover:text-slate-700",
)}
>
{tab}
</button>
))}
</div>

<Tabs defaultValue="gpa">
<TabsList>
<TabsTrigger value="gpa">GPA ์„ฑ์ </TabsTrigger>
<TabsTrigger value="language">์–ดํ•™์„ฑ์ </TabsTrigger>
</TabsList>
<div className="mt-6 flex flex-wrap items-center gap-2">
<h2 className="mr-auto text-xl font-bold tracking-[-0.02em] text-slate-800">๊ถŒ์—ญ/๋‚˜๋ผ</h2>

<TabsContent value="gpa">
<GpaScoreTable verifyFilter={verifyFilter} />
</TabsContent>
<div className="relative min-w-[300px] flex-1">
<Input
value={searchKeyword}
onChange={(event) => setSearchKeyword(event.target.value)}
placeholder="๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•œ ํ›„ ๊ฒ€์ƒ‰ํ•  ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”"
className="h-9 border-slate-200 bg-slate-50 pr-10 text-xs"
/>
<Search className="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" />
</div>

<TabsContent value="language">
<LanguageScoreTable verifyFilter={verifyFilter} />
</TabsContent>
</Tabs>
<Button
type="button"
className="h-9 rounded-md bg-indigo-500 px-3 text-xs text-white hover:bg-indigo-600"
>
<SquarePen className="h-3.5 w-3.5" />
๊ถŒ์—ญ/๋‚˜๋ผ ์ƒ์„ฑํ•˜๊ธฐ
</Button>
</div>

<div className="mt-4 flex items-center gap-4">
{verifyFilters.map((option) => (
<button
key={option.value}
type="button"
onClick={() => setVerifyFilter(option.value)}
className={cn(
"inline-flex items-center gap-2 text-xs font-medium transition-colors",
verifyFilter === option.value ? "text-indigo-600" : "text-slate-400 hover:text-slate-600",
)}
>
<span
className={cn(
"h-2.5 w-2.5 rounded-full border",
verifyFilter === option.value ? "border-indigo-500 bg-indigo-500" : "border-slate-300 bg-white",
)}
/>
{option.label}
</button>
))}
</div>

<div className="mt-5 rounded-xl border border-slate-100 bg-white p-3">
<Tabs defaultValue="gpa">
<TabsList className="h-9 rounded-md bg-slate-100 p-1">
<TabsTrigger
value="gpa"
className="rounded-md px-4 text-xs font-semibold data-[state=active]:bg-white data-[state=active]:text-indigo-600"
>
GPA ์„ฑ์ 
</TabsTrigger>
<TabsTrigger
value="language"
className="rounded-md px-4 text-xs font-semibold data-[state=active]:bg-white data-[state=active]:text-indigo-600"
>
์–ดํ•™์„ฑ์ 
</TabsTrigger>
</TabsList>

<TabsContent value="gpa" className="mt-3">
<GpaScoreTable verifyFilter={verifyFilter} />
</TabsContent>

<TabsContent value="language" className="mt-3">
<LanguageScoreTable verifyFilter={verifyFilter} />
</TabsContent>
</Tabs>
</div>
</div>
</section>
</div>
</div>
);
}