OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
 
master @ 356 LINES
 
[ HISTORY ]  [ UP ]
 

---
import PageLayout from "../layouts/PageLayout.astro";
import PlayerSearch from "../components/PlayerSearch/PlayerSearch.astro";
import LeaderboardTypeNav from "../components/Leaderboard/LeaderboardTypeNav/Leade
rboardTypeNav.astro";
import RecentRuns from "../components/Home/RecentRuns/RecentRuns.astro";
import DungeonRecordCard from "../components/Home/DungeonRecordCard/DungeonRecordC
ard.astro";
import TopPlayersList from "../components/Home/TopPlayersList/TopPlayersList.astro
";
import Carousel from "../components/Home/Carousel/Carousel.astro";

export const prerender = false;

let home: any = null;
try {
  const response = await fetch(`${Astro.url.origin}/api/home.json`);
  if (response.ok) {
    home = await response.json();
  }
} catch (error) {
  console.error("[Home] Failed to load home.json:", error);
}

// Seasons sorted descending so the latest is the default tab.
const seasonKeys = home
  ? Object.keys(home.seasons).sort((a, b) => parseInt(b) - parseInt(a))
  : [];
const defaultSeasonKey = seasonKeys[0];

// render every season server-side; client toggles visibility for instant switch
const seasons = seasonKeys.map((key) => {
  const s = home.seasons[key];
  return {
    key,
    label: s.season_name || `Season ${key}`,
    data: s,
    dungeonEntries: Object.entries(
      s.top_runs_per_dungeon as Record<string, any[]>,
    )
      .filter(([, runs]) => runs && runs.length > 0)
      .sort(
        ([, a], [, b]) => (a[0]?.dungeon_id ?? 0) - (b[0]?.dungeon_id ?? 0),
      ),
  };
});

const recentRuns = home?.recent_top_runs ?? [];
const generatedAt = home?.generated_at ? new Date(home.generated_at) : null;
---

<PageLayout
  title="WoW Mists of Pandaria Challenge Mode Leaderboards"
  description="Top challenge mode times, recent record-setting runs, and the world
's best players for World of Warcraft: Mists of Pandaria Classic."
>
  <div class="home">
    {
      !home && (
        <div class="home__error">
          <h2>Couldn't load home data</h2>
          <p>
            The home.json file is missing or unreachable. Try regenerating it
            via <code>ookstats generate home</code>.
          </p>
        </div>
      )
    }

    {
      home && (
        <>
          <header class="home__hero">
            <h1>Challenge Mode Leaderboards</h1>
            {generatedAt && (
              <span class="home__updated">
                Data updated{" "}
                {generatedAt.toLocaleString("en-US", {
                  month: "short",
                  day: "numeric",
                  hour: "2-digit",
                  minute: "2-digit",
                })}
              </span>
            )}
          </header>

          <PlayerSearch />

          <LeaderboardTypeNav
            currentTab="home"
            currentSeason={parseInt(defaultSeasonKey)}
          />

          {seasons.length > 1 && (
            <nav class="home__season-tabs" role="tablist" aria-label="Season">
              {seasons.map((s) => (
                <button
                  type="button"
                  class:list={[
                    "home__season-tab",
                    { active: s.key === defaultSeasonKey },
                  ]}
                  role="tab"
                  aria-selected={s.key === defaultSeasonKey ? "true" : "false"}
                  data-season-key={s.key}
                >
                  {s.label}
                </button>
              ))}
            </nav>
          )}

          {seasons.map((s) => (
            <div
              class:list={[
                "home__season-panel",
                { hidden: s.key !== defaultSeasonKey },
              ]}
              data-season-panel={s.key}
              aria-hidden={s.key !== defaultSeasonKey ? "true" : "false"}
            >
              <section class="home__section">
                <header class="home__section-header">
                  <h2>Top Players - {s.label}</h2>
                </header>
                <Carousel
                  ariaLabel={`Top players by region - ${s.label}`}
                  class="home__players-carousel"
                  perView={3}
                  perViewMobile={1}
                >
                  <TopPlayersList
                    title="Global"
                    scope="global"
                    players={s.data.top_players.global}
                    viewAllHref={`/challenge-mode/season${s.key}/players/global`}
                  />
                  <TopPlayersList
                    title="US"
                    scope="regional"
                    players={s.data.top_players.us}
                    viewAllHref={`/challenge-mode/season${s.key}/players/us`}
                  />
                  <TopPlayersList
                    title="EU"
                    scope="regional"
                    players={s.data.top_players.eu}
                    viewAllHref={`/challenge-mode/season${s.key}/players/eu`}
                  />
                  <TopPlayersList
                    title="KR"
                    scope="regional"
                    players={s.data.top_players.kr}
                    viewAllHref={`/challenge-mode/season${s.key}/players/kr`}
                  />
                  <TopPlayersList
                    title="TW"
                    scope="regional"
                    players={s.data.top_players.tw}
                    viewAllHref={`/challenge-mode/season${s.key}/players/tw`}
                  />
                </Carousel>
              </section>

              <section class="home__section">
                <header class="home__section-header">
                  <h2>Dungeon Records - {s.label}</h2>
                </header>
                <Carousel
                  ariaLabel={`Dungeon records - ${s.label}`}
                  class="home__dungeons-carousel"
                  perView={2}
                  perViewMobile={1}
                >
                  {s.dungeonEntries.map(([slug, runs]) => (
                    <DungeonRecordCard
                      dungeonName={runs[0].dungeon_name}
                      dungeonSlug={slug}
                      runs={runs}
                      seasonId={parseInt(s.key)}
                    />
                  ))}
                </Carousel>
              </section>
            </div>
          ))}

          <section class="home__section">
            <RecentRuns runs={recentRuns} />
          </section>
        </>
      )
    }
  </div>
</PageLayout>

<script>
  // toggle which server-rendered season-panel is visible
  function wireSeasonTabs() {
    const tabs =
      document.querySelectorAll<HTMLButtonElement>(".home__season-tab");
    const panels = document.querySelectorAll<HTMLElement>(
      ".home__season-panel",
    );
    if (tabs.length === 0) return;
    tabs.forEach((tab) => {
      tab.addEventListener("click", () => {
        const key = tab.dataset.seasonKey;
        if (!key) return;
        tabs.forEach((t) => {
          const active = t === tab;
          t.classList.toggle("active", active);
          t.setAttribute("aria-selected", active ? "true" : "false");
        });
        panels.forEach((p) => {
          const isActive = p.dataset.seasonPanel === key;
          p.classList.toggle("hidden", !isActive);
          p.setAttribute("aria-hidden", isActive ? "false" : "true");
        });
        // carousel arrows read scrollWidth, which is 0 while hidden;
        // resize event makes them re-evaluate after reveal
        window.dispatchEvent(new Event("resize"));
      });
    });
  }
  wireSeasonTabs();
  document.addEventListener("astro:page-load", wireSeasonTabs);
</script>

<style lang="scss">
  @use "../styles/core/tokens" as *;

  // matches .leaderboard-page wrapper on /challenge-mode/* for visual cohesion
  .home {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
  }

  .home__error {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: $radius-lg;
    padding: $spacing-2xl;
    text-align: center;
    color: var(--text-secondary);
    code {
      background: var(--bg-primary);
      padding: 2px 6px;
      border-radius: $radius-sm;
      color: var(--highlight-color);
    }
  }

  // mirrors LeaderboardHeader spacing/typography
  .home__hero {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: $spacing-lg;
    flex-wrap: wrap;
    margin: 8px 0 16px 0;

    h1 {
      margin: 0;
      font-size: 2em;
      color: var(--text-primary);
      font-weight: $font-weight-bold;
    }
  }

  .home__updated {
    color: var(--text-tertiary);
    font-size: $font-size-sm;
  }

  // pill-style tabs matching the stats page region tabs
  .home__season-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: $spacing-xs;
    margin-bottom: $spacing-lg;
  }

  .home__season-tab {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
    padding: 6px 14px;
    cursor: pointer;
    font-size: $font-size-sm;
    font-weight: $font-weight-semibold;
    border-radius: $radius-md;
    transition:
      color 0.15s ease,
      background 0.15s ease,
      border-color 0.15s ease;

    &:hover {
      color: var(--text-primary);
      border-color: var(--highlight-color);
    }

    &.active {
      color: var(--bg-primary);
      background: var(--highlight-color);
      border-color: var(--highlight-color);
    }
  }

  .home__season-panel {
    display: flex;
    flex-direction: column;
    gap: $spacing-2xl;

    &.hidden {
      display: none;
    }
  }

  .home__section {
    display: flex;
    flex-direction: column;
    gap: $spacing-lg;

    & + &,
    // The Recent Runs section sits as a sibling of the season-panel wrapper,
    // not another `.home__section`, so the adjacency selector above wouldn't
    // reach it without this second rule. Keeps section rhythm consistent.
    .home__season-panel + & {
      margin-top: $spacing-2xl;
    }
  }

  .home__section-header {
    h2 {
      margin: 0;
      font-size: $font-size-2xl;
      color: var(--text-primary);
      font-weight: $font-weight-semibold;
    }
  }

  // Carousel handles per-card sizing via its `perView` prop (see Carousel.scss).
  // No per-card overrides needed here.

  // mobile overrides at end of stylesheet to win the cascade
  @media (max-width: 768px) {
    .home {
      padding: 15px;
    }

    // Mirror LeaderboardHeader's mobile font-size drop (~1.5em).
    .home__hero h1 {
      font-size: 1.5em;
    }
  }
</style>

---
import PageLayout from "../layouts/PageLayou
t.astro";
import PlayerSearch from "../components/Play
erSearch/PlayerSearch.astro";
import LeaderboardTypeNav from "../component
s/Leaderboard/LeaderboardTypeNav/Leaderboard
TypeNav.astro";
import RecentRuns from "../components/Home/R
ecentRuns/RecentRuns.astro";
import DungeonRecordCard from "../components
/Home/DungeonRecordCard/DungeonRecordCard.as
tro";
import TopPlayersList from "../components/Ho
me/TopPlayersList/TopPlayersList.astro";
import Carousel from "../components/Home/Car
ousel/Carousel.astro";

export const prerender = false;

let home: any = null;
try {
  const response = await fetch(`${Astro.url.
origin}/api/home.json`);
  if (response.ok) {
    home = await response.json();
  }
} catch (error) {
  console.error("[Home] Failed to load home.
json:", error);
}

// Seasons sorted descending so the latest i
s the default tab.
const seasonKeys = home
  ? Object.keys(home.seasons).sort((a, b) =>
 parseInt(b) - parseInt(a))
  : [];
const defaultSeasonKey = seasonKeys[0];

// render every season server-side; client t
oggles visibility for instant switch
const seasons = seasonKeys.map((key) => {
  const s = home.seasons[key];
  return {
    key,
    label: s.season_name || `Season ${key}`,
    data: s,
    dungeonEntries: Object.entries(
      s.top_runs_per_dungeon as Record<strin
g, any[]>,
    )
      .filter(([, runs]) => runs && runs.len
gth > 0)
      .sort(
        ([, a], [, b]) => (a[0]?.dungeon_id 
?? 0) - (b[0]?.dungeon_id ?? 0),
      ),
  };
});

const recentRuns = home?.recent_top_runs ?? 
[];
const generatedAt = home?.generated_at ? new
 Date(home.generated_at) : null;
---

<PageLayout
  title="WoW Mists of Pandaria Challenge Mod
e Leaderboards"
  description="Top challenge mode times, rec
ent record-setting runs, and the world's bes
t players for World of Warcraft: Mists of Pa
ndaria Classic."
>
  <div class="home">
    {
      !home && (
        <div class="home__error">
          <h2>Couldn't load home data</h2>
          <p>
            The home.json file is missing or
 unreachable. Try regenerating it
            via <code>ookstats generate home
</code>.
          </p>
        </div>
      )
    }

    {
      home && (
        <>
          <header class="home__hero">
            <h1>Challenge Mode Leaderboards<
/h1>
            {generatedAt && (
              <span class="home__updated">
                Data updated{" "}
                {generatedAt.toLocaleString(
"en-US", {
                  month: "short",
                  day: "numeric",
                  hour: "2-digit",
                  minute: "2-digit",
                })}
              </span>
            )}
          </header>

          <PlayerSearch />

          <LeaderboardTypeNav
            currentTab="home"
            currentSeason={parseInt(defaultS
easonKey)}
          />

          {seasons.length > 1 && (
            <nav class="home__season-tabs" r
ole="tablist" aria-label="Season">
              {seasons.map((s) => (
                <button
                  type="button"
                  class:list={[
                    "home__season-tab",
                    { active: s.key === defa
ultSeasonKey },
                  ]}
                  role="tab"
                  aria-selected={s.key === d
efaultSeasonKey ? "true" : "false"}
                  data-season-key={s.key}
                >
                  {s.label}
                </button>
              ))}
            </nav>
          )}

          {seasons.map((s) => (
            <div
              class:list={[
                "home__season-panel",
                { hidden: s.key !== defaultS
easonKey },
              ]}
              data-season-panel={s.key}
              aria-hidden={s.key !== default
SeasonKey ? "true" : "false"}
            >
              <section class="home__section"
>
                <header class="home__section
-header">
                  <h2>Top Players - {s.label
}</h2>
                </header>
                <Carousel
                  ariaLabel={`Top players by
 region - ${s.label}`}
                  class="home__players-carou
sel"
                  perView={3}
                  perViewMobile={1}
                >
                  <TopPlayersList
                    title="Global"
                    scope="global"
                    players={s.data.top_play
ers.global}
                    viewAllHref={`/challenge
-mode/season${s.key}/players/global`}
                  />
                  <TopPlayersList
                    title="US"
                    scope="regional"
                    players={s.data.top_play
ers.us}
                    viewAllHref={`/challenge
-mode/season${s.key}/players/us`}
                  />
                  <TopPlayersList
                    title="EU"
                    scope="regional"
                    players={s.data.top_play
ers.eu}
                    viewAllHref={`/challenge
-mode/season${s.key}/players/eu`}
                  />
                  <TopPlayersList
                    title="KR"
                    scope="regional"
                    players={s.data.top_play
ers.kr}
                    viewAllHref={`/challenge
-mode/season${s.key}/players/kr`}
                  />
                  <TopPlayersList
                    title="TW"
                    scope="regional"
                    players={s.data.top_play
ers.tw}
                    viewAllHref={`/challenge
-mode/season${s.key}/players/tw`}
                  />
                </Carousel>
              </section>

              <section class="home__section"
>
                <header class="home__section
-header">
                  <h2>Dungeon Records - {s.l
abel}</h2>
                </header>
                <Carousel
                  ariaLabel={`Dungeon record
s - ${s.label}`}
                  class="home__dungeons-caro
usel"
                  perView={2}
                  perViewMobile={1}
                >
                  {s.dungeonEntries.map(([sl
ug, runs]) => (
                    <DungeonRecordCard
                      dungeonName={runs[0].d
ungeon_name}
                      dungeonSlug={slug}
                      runs={runs}
                      seasonId={parseInt(s.k
ey)}
                    />
                  ))}
                </Carousel>
              </section>
            </div>
          ))}

          <section class="home__section">
            <RecentRuns runs={recentRuns} />
          </section>
        </>
      )
    }
  </div>
</PageLayout>

<script>
  // toggle which server-rendered season-pan
el is visible
  function wireSeasonTabs() {
    const tabs =
      document.querySelectorAll<HTMLButtonEl
ement>(".home__season-tab");
    const panels = document.querySelectorAll
<HTMLElement>(
      ".home__season-panel",
    );
    if (tabs.length === 0) return;
    tabs.forEach((tab) => {
      tab.addEventListener("click", () => {
        const key = tab.dataset.seasonKey;
        if (!key) return;
        tabs.forEach((t) => {
          const active = t === tab;
          t.classList.toggle("active", activ
e);
          t.setAttribute("aria-selected", ac
tive ? "true" : "false");
        });
        panels.forEach((p) => {
          const isActive = p.dataset.seasonP
anel === key;
          p.classList.toggle("hidden", !isAc
tive);
          p.setAttribute("aria-hidden", isAc
tive ? "false" : "true");
        });
        // carousel arrows read scrollWidth,
 which is 0 while hidden;
        // resize event makes them re-evalua
te after reveal
        window.dispatchEvent(new Event("resi
ze"));
      });
    });
  }
  wireSeasonTabs();
  document.addEventListener("astro:page-load
", wireSeasonTabs);
</script>

<style lang="scss">
  @use "../styles/core/tokens" as *;

  // matches .leaderboard-page wrapper on /c
hallenge-mode/* for visual cohesion
  .home {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
  }

  .home__error {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: $radius-lg;
    padding: $spacing-2xl;
    text-align: center;
    color: var(--text-secondary);
    code {
      background: var(--bg-primary);
      padding: 2px 6px;
      border-radius: $radius-sm;
      color: var(--highlight-color);
    }
  }

  // mirrors LeaderboardHeader spacing/typog
raphy
  .home__hero {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: $spacing-lg;
    flex-wrap: wrap;
    margin: 8px 0 16px 0;

    h1 {
      margin: 0;
      font-size: 2em;
      color: var(--text-primary);
      font-weight: $font-weight-bold;
    }
  }

  .home__updated {
    color: var(--text-tertiary);
    font-size: $font-size-sm;
  }

  // pill-style tabs matching the stats page
 region tabs
  .home__season-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: $spacing-xs;
    margin-bottom: $spacing-lg;
  }

  .home__season-tab {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
    padding: 6px 14px;
    cursor: pointer;
    font-size: $font-size-sm;
    font-weight: $font-weight-semibold;
    border-radius: $radius-md;
    transition:
      color 0.15s ease,
      background 0.15s ease,
      border-color 0.15s ease;

    &:hover {
      color: var(--text-primary);
      border-color: var(--highlight-color);
    }

    &.active {
      color: var(--bg-primary);
      background: var(--highlight-color);
      border-color: var(--highlight-color);
    }
  }

  .home__season-panel {
    display: flex;
    flex-direction: column;
    gap: $spacing-2xl;

    &.hidden {
      display: none;
    }
  }

  .home__section {
    display: flex;
    flex-direction: column;
    gap: $spacing-lg;

    & + &,
    // The Recent Runs section sits as a sib
ling of the season-panel wrapper,
    // not another `.home__section`, so the 
adjacency selector above wouldn't
    // reach it without this second rule. Ke
eps section rhythm consistent.
    .home__season-panel + & {
      margin-top: $spacing-2xl;
    }
  }

  .home__section-header {
    h2 {
      margin: 0;
      font-size: $font-size-2xl;
      color: var(--text-primary);
      font-weight: $font-weight-semibold;
    }
  }

  // Carousel handles per-card sizing via it
s `perView` prop (see Carousel.scss).
  // No per-card overrides needed here.

  // mobile overrides at end of stylesheet t
o win the cascade
  @media (max-width: 768px) {
    .home {
      padding: 15px;
    }

    // Mirror LeaderboardHeader's mobile fon
t-size drop (~1.5em).
    .home__hero h1 {
      font-size: 1.5em;
    }
  }
</style>
 
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET