diff --git a/atcoder-problems-frontend/src/pages/TablePage/index.tsx b/atcoder-problems-frontend/src/pages/TablePage/index.tsx index ddaf4a2f..297e3744 100644 --- a/atcoder-problems-frontend/src/pages/TablePage/index.tsx +++ b/atcoder-problems-frontend/src/pages/TablePage/index.tsx @@ -20,6 +20,7 @@ import { loggedInUserId } from "../../utils/UserState"; import { classifyContest, ContestCategory, + isHiddenContest, } from "../../utils/ContestClassifier"; import { getLikeContestCategory } from "../../utils/LikeContestUtils"; import { TableTabButtons } from "./TableTab"; @@ -87,6 +88,9 @@ export const TablePage: React.FC = (props) => { return []; } return contests.filter((contest) => { + if (isHiddenContest(contest)) { + return false; + } const contestType = classifyContest(contest); if (contestType === activeTab) { return true; @@ -130,6 +134,7 @@ export const TablePage: React.FC = (props) => { "ARC", "AGC", "AWC", + "ADT", "ABC-Like", "ARC-Like", "AGC-Like", @@ -149,6 +154,8 @@ export const TablePage: React.FC = (props) => { ? "AtCoder Grand Contest" : activeTab === "AWC" ? "AtCoder Weekday Contest" + : activeTab === "ADT" + ? "AtCoder Daily Training" : activeTab === "PAST" ? "PAST" : `${activeTab} Contest` diff --git a/atcoder-problems-frontend/src/utils/ContestClassifier.test.ts b/atcoder-problems-frontend/src/utils/ContestClassifier.test.ts index dcc86212..f9ec1b44 100644 --- a/atcoder-problems-frontend/src/utils/ContestClassifier.test.ts +++ b/atcoder-problems-frontend/src/utils/ContestClassifier.test.ts @@ -2,6 +2,7 @@ import Contest from "../interfaces/Contest"; import { isRatedContest, classifyContest, + isHiddenContest, ContestCategory, } from "./ContestClassifier"; @@ -63,6 +64,17 @@ describe("test function classifyContest", () => { expect(classifyContest(adtContest)).toBe("ADT" as ContestCategory); }); + it("when adt_top is not classified as ADT", () => { + const adtTopContest: Contest = { + duration_second: 0, + id: "adt_top", + rate_change: "-", + start_epoch_second: 0, + title: "AtCoder Daily Training", + }; + expect(classifyContest(adtTopContest)).not.toBe("ADT" as ContestCategory); + }); + it("when ABC-like", () => { const abcLikeContest: Contest = { duration_second: 6000, @@ -100,3 +112,27 @@ describe("test function classifyContest", () => { ); }); }); + +describe("test function isHiddenContest", () => { + it("when adt_top is hidden", () => { + const adtTopContest: Contest = { + duration_second: 0, + id: "adt_top", + rate_change: "-", + start_epoch_second: 0, + title: "AtCoder Daily Training", + }; + expect(isHiddenContest(adtTopContest)).toBe(true); + }); + + it("when a normal ADT contest is not hidden", () => { + const adtContest: Contest = { + duration_second: 6000, + id: "adt_all_20260612_2", + rate_change: "-", + start_epoch_second: 1781260800, + title: "AtCoder Daily Training 2026/06/12 All", + }; + expect(isHiddenContest(adtContest)).toBe(false); + }); +}); diff --git a/atcoder-problems-frontend/src/utils/ContestClassifier.ts b/atcoder-problems-frontend/src/utils/ContestClassifier.ts index ffe342c4..ac7a1aaf 100644 --- a/atcoder-problems-frontend/src/utils/ContestClassifier.ts +++ b/atcoder-problems-frontend/src/utils/ContestClassifier.ts @@ -22,6 +22,11 @@ export type ContestCategory = typeof ContestCategories[number]; export const AGC_001_START = 1468670400; +// `adt_top` is the ADT top page, not an actual daily training round, so it +// should be hidden from contest listings. +export const isHiddenContest = (contest: Contest): boolean => + contest.id === "adt_top"; + export const isRatedContest = ( contest: Contest, problemCount: number @@ -61,7 +66,7 @@ export const classifyContest = ( if (/^awc\d{4}$/.exec(contest.id)) { return "AWC"; } - if (/^adt_/.exec(contest.id)) { + if (/^adt_/.exec(contest.id) && !isHiddenContest(contest)) { return "ADT"; } if (