Skip to content
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
2 changes: 2 additions & 0 deletions src/api/client/getRanking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ export interface RankingRow {
baekjoonId: string;
team: string;
diff: number;
newUser: boolean;
}

export interface RankingPage {
hasNext: boolean;
updateTime: string;
rows: RankingRow[];
}

Expand Down
Binary file added src/assets/images/grass.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/new1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/new3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/new444.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 21 additions & 5 deletions src/components/ranking/RankingRow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getTierImageUrl } from "../../constants/tierMap";
import { getTeamLabel, type TeamName } from "@/types/team";
import newPng from "@/assets/images/new444.png";

/**
* author : 박준희
Expand All @@ -14,6 +15,7 @@ export interface RankingRowData {
baekjoonId: string;
team: TeamName;
diff: number;
newUser: boolean;
}

interface RankingRowProps {
Expand All @@ -22,12 +24,26 @@ interface RankingRowProps {

const RankingRow = ({ row }: RankingRowProps) => {
return (
<div className="grid grid-cols-7 items-center py-4 border-b border-gray-200 text-sm">


<div className="text-center">{row.rank}등</div>
<div
className={[
"relative grid grid-cols-7 items-center py-4 border-b text-sm",
row.newUser
? "border-green-100 bg-gradient-to-r from-green-100/80 to-transparent"
: "border-blue-200",
].join(" ")}
>
<div className="relative text-center">
{row.newUser && (
<img
src={newPng}
alt="new"
className="absolute left-3 top-1/2 -translate-y-1/2 w-6 h-6 object-contain"
/>
)}
<span className="tabular-nums">{row.rank}등</span>
</div>

<div className="flex items-center gap-2 pl-3">
<div className="flex items-center gap-2">
<img src={getTierImageUrl(row.tier)} className="w-5 h-5" />
<span>{row.name}</span>
</div>
Expand Down
31 changes: 31 additions & 0 deletions src/components/ranking/RankingUpdateTime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* author : 박준희
*/
type Props = {
updateTime?: string | null; // "2025-12-24T19:00:00"
};

const formatUpdateTime = (value: string) => {
const date = new Date(value);
if (isNaN(date.getTime())) {
return `${value} KST 기준`;
}

const yyyy = date.getFullYear();
const mm = date.getMonth() + 1;
const dd = date.getDate();
const hh = String(date.getHours()).padStart(2, '0');
const mi = String(date.getMinutes()).padStart(2, '0');

return `${yyyy}년 ${mm}월 ${dd}일 ${hh}:${mi} KST 기준`;
};
Comment on lines 8 to 21

Choose a reason for hiding this comment

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

medium

formatUpdateTime 함수에서 정규식을 사용하여 날짜 문자열을 파싱하고 있습니다. 이 방식은 날짜 형식이 조금만 바뀌어도(예: 초 단위나 타임존 정보 포함) 코드가 깨지기 쉬운 단점이 있습니다. JavaScript/TypeScript의 내장 new Date() 생성자를 사용하는 것이 더 안정적이고 표준적인 방법입니다.

const formatUpdateTime = (value: string) => {
  const date = new Date(value);
  if (isNaN(date.getTime())) {
    return `${value} KST 기준`;
  }

  const yyyy = date.getFullYear();
  const mm = date.getMonth() + 1;
  const dd = date.getDate();
  const hh = String(date.getHours()).padStart(2, '0');
  const mi = String(date.getMinutes()).padStart(2, '0');

  return `${yyyy}년 ${mm}월 ${dd}일 ${hh}:${mi} KST 기준`;
};


export default function RankingUpdateTime({ updateTime }: Props) {
if (!updateTime) return null;

return (
<div className="flex justify-center items-center mb-2">

Choose a reason for hiding this comment

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

medium

RankingUpdateTime 컴포넌트의 최상위 divmb-2 클래스가 적용되어 있습니다. 일반적으로 컴포넌트 자체에서 외부 여백을 지정하면 재사용성이 떨어집니다. 컴포넌트를 사용하는 부모가 레이아웃과 간격을 책임지는 것이 더 좋은 구조입니다. 이미 부모 컴포넌트인 RankingPage에서 gap-3를 사용해 간격을 조절하고 있으므로, 이 클래스는 불필요해 보입니다.

    <div className="flex justify-center items-center">

<span className="text-xs text-gray-500">{formatUpdateTime(updateTime)}</span>
</div>
);
}
9 changes: 8 additions & 1 deletion src/pages/RankingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import RankingDateDisplay from "../components/ranking/RankingDateDisplay";
import RankingGroupSelector from "../components/ranking/RankingGroupSelector";
import Calendar from "../components/common/Calendar";
import RankingHeader from "../components/ranking/RankingHeader";
import RankingUpdateTime from "../components/ranking/RankingUpdateTime";

import { useFetch } from "../hooks/useFetch";
import { getRanking, type RankingPage as RankingPageType } from "../api/client/getRanking";
Expand All @@ -27,6 +28,7 @@ export default function RankingPage() {
const [page, setPage] = useState(1);
const [rows, setRows] = useState<RankingRowData[]>([]);
const [hasMore, setHasMore] = useState(true);
const [updateTime, setUpdateTime] = useState<string>("");

const loaderRef = useRef<HTMLDivElement | null>(null);
const SIZE = 20;
Expand Down Expand Up @@ -85,6 +87,7 @@ export default function RankingPage() {
if (!data) return;

setHasMore(data.hasNext);
setUpdateTime(data.updateTime);

const mapped: RankingRowData[] = data.rows.map((row) => ({
...row,
Expand Down Expand Up @@ -141,7 +144,11 @@ export default function RankingPage() {
</div>

<div className="flex items-center justify-between mb-6">
<RankingPeriodSelector value={period} onChange={handlePeriodChange} />
<div className="flex items-end gap-3">
<RankingPeriodSelector value={period} onChange={handlePeriodChange} />
<RankingUpdateTime updateTime={updateTime} />
</div>

<RankingGroupSelector value={group} onChange={handleGroupChange} />
</div>
</div>
Expand Down
Loading