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
Binary file added loading-screens-and-fixes.tar.gz
Binary file not shown.
35 changes: 35 additions & 0 deletions src/app/events/[event_key]/EmptyTabState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* EmptyTabState
*
* Small banner used at the top of the tabs that don't have real TBA
* data wired up yet (Matches / Teams / Rankings / Awards). Sits above
* a skeleton mockup of that tab's future layout so the screen reads
* as "finished, waiting on data" instead of "unfinished".
*/
import { LucideIcon } from "lucide-react";

interface EmptyTabStateProps {
icon: LucideIcon;
title: string;
description: string;
}

export default function EmptyTabState({ icon: Icon, title, description }: EmptyTabStateProps) {
return (
<div className="flex items-center gap-4 rounded-2xl border border-dashed border-slate-800 bg-slate-900/40 p-5">
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-blue-500/10 text-blue-400">
<Icon className="h-5 w-5" />
</div>

<div>
<p className="text-sm font-medium text-white">
{title}
</p>

<p className="mt-0.5 text-sm text-slate-400">
{description}
</p>
</div>
</div>
);
}
51 changes: 51 additions & 0 deletions src/app/events/[event_key]/EventAwardsTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* EventAwardsTab
*
* Awards tab for `/events/[event_key]`. Defaults to `loading = true`
* (skeleton) since there's no real data source wired up yet — once
* the TBA awards endpoint is adapted into `EventAward[]`
* (`src/types/events.ts`), pass it in:
*
* <EventAwardsTab awards={event.awards} loading={false} />
*/
import { Trophy } from "lucide-react";
import { EventAward } from "@/types/events";
import AwardsList from "@/components/events/event/awards/AwardsList";
import EmptyTabState from "./EmptyTabState";

interface EventAwardsTabProps {
awards?: EventAward[];
loading?: boolean;
}

export default function EventAwardsTab({ awards = [], loading = true }: EventAwardsTabProps) {
const isEmpty = !loading && awards.length === 0;

return (
<div className="space-y-6">
{(loading || isEmpty) && (
<EmptyTabState
icon={Trophy}
title={loading ? "Awards not announced yet" : "No awards yet"}
description={
loading
? "Award winners will be listed here once they're announced at the event."
: "This event doesn't have any awards recorded yet."
}
/>
)}

<section className="rounded-2xl border border-slate-800 bg-slate-900 p-6">
<h2 className="text-lg font-semibold text-white">
Awards
</h2>

{!isEmpty && (
<div className="mt-5">
<AwardsList awards={awards} loading={loading} />
</div>
)}
</section>
</div>
);
}
73 changes: 39 additions & 34 deletions src/app/events/[event_key]/EventKeyClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,26 @@
* and then swaps in a per-tab content block based on the `tab` prop
* that `page.tsx` already validated against `EventTab`.
*
* Scope note: this only wires up the TAB STRUCTURE. Every tab besides
* "Scout" (which just deep-links into the existing `/scout` flow) is
* a placeholder — hooking each one up to real TBA data (matches,
* teams, rankings, awards) is deliberately left for a follow-up so
* this change stays focused on navigation, not the API layer.
* Scope note: this only wires up the TAB STRUCTURE + DESIGN. Every
* tab besides "Overview" and "Scout" (which just deep-links into the
* existing `/scout` flow) renders a design-finished skeleton mockup
* of its future layout — hooking each one up to real TBA data
* (matches, teams, rankings, awards) is deliberately left for a
* follow-up so this change stays focused on the screen's visuals,
* not the API layer.
*/

import Link from "next/link";
import { ArrowRight, ClipboardList } from "lucide-react";
import EventHeader from "@/components/events/event/EventHeader";
import { EventFull, EventTab, EventWebcastType, WebcastUrl} from "@/types/events";
import { EventFull, EventTab, EventTeamSummary, EventWebcastType, WebcastUrl} from "@/types/events";
import EventOverview from "./EventOverviewTab";
import EventMatchesTab from "./EventMatchesTab";
import EventTeamsTab from "./EventTeamsTab";
import EventRankingsTab from "./EventRankingsTab";
import EventAwardsTab from "./EventAwardsTab";
import { EventInfoProps } from "@/components/events/event/EventInfo";
import { getTeamSummary } from "@/lib/api/teams";

Check warning on line 29 in src/app/events/[event_key]/EventKeyClient.tsx

View workflow job for this annotation

GitHub Actions / build

'getTeamSummary' is defined but never used

Check warning on line 29 in src/app/events/[event_key]/EventKeyClient.tsx

View workflow job for this annotation

GitHub Actions / build

'getTeamSummary' is defined but never used


export const mockInformation: EventInfoProps = {
Expand Down Expand Up @@ -54,56 +62,53 @@
eventKey: string;
tab: EventTab;
event: EventFull;
teams: EventTeamSummary[] | null;
}

/** Simple placeholder shown for tabs that aren't wired to real data yet. */
function ComingSoon({ label }: { label: string }) {
return (
<div className="flex h-48 flex-col items-center justify-center gap-2 rounded-2xl border border-dashed border-slate-800 text-slate-500">
<p className="text-sm font-medium">
{label} — coming soon
</p>

<p className="text-xs text-slate-600">
This tab will be wired up to TBA data in a follow-up.
</p>
</div>
);
}

function EventTabContent({ eventKey, tab, event}: EventKeyProps) {
async function EventTabContent({ eventKey, tab, event, teams}: EventKeyProps) {
switch (tab) {
case EventTab.Overview:
return <EventOverview event={event} />

case EventTab.Matches:
return <ComingSoon label="Matches" />;
return <EventMatchesTab />;

case EventTab.Teams:
return <ComingSoon label="Teams" />;
case EventTab.Teams:
return <EventTeamsTab teams={teams ? teams : []} loading={true} />;

case EventTab.Rankings:
return <ComingSoon label="Rankings" />;
return <EventRankingsTab/>;

case EventTab.Awards:
return <ComingSoon label="Awards" />;
return <EventAwardsTab />;

case EventTab.Scout:
// This one doesn't need TBA data at all — it just hands the
// scouter off to the existing scout-prep flow with the
// current event pre-selected via the `?event=` search param
// that `scoutPageClient.tsx` already reads.
return (
<div className="flex h-48 flex-col items-center justify-center gap-4 rounded-2xl border border-slate-800 text-slate-400">
<p className="text-sm">
Ready to scout this event?
</p>
<div className="flex flex-col items-center justify-center gap-4 rounded-2xl border border-slate-800 bg-slate-900 px-6 py-12 text-center">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-blue-500/10 text-blue-400">
<ClipboardList className="h-6 w-6" />
</div>

<div>
<p className="text-base font-medium text-white">
Ready to scout this event?
</p>

<p className="mt-1 text-sm text-slate-400">
Jump into the scouting flow with this event already selected.
</p>
</div>

<Link
href={`/scout?event=${eventKey}`}
className="rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition hover:bg-blue-500"
className="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition hover:bg-blue-500"
>
Go to Scout Preparation
<ArrowRight className="h-4 w-4" />
</Link>
</div>
);
Expand All @@ -113,11 +118,11 @@
}
}

export default function EventKeyClient({ eventKey, tab, event }: EventKeyProps) {
export default function EventKeyClient({ eventKey, tab, event, teams }: EventKeyProps) {
return (
<main className="mx-auto flex w-full max-w-7xl flex-col gap-6 overflow-x-hidden px-4 py-6 sm:px-6">
<EventHeader eventKey={eventKey} activeTab={tab} event={event} />
<EventTabContent eventKey={eventKey} tab={tab} event={event} />
<EventTabContent eventKey={eventKey} tab={tab} event={event} teams={teams} />
</main>
);
}
63 changes: 63 additions & 0 deletions src/app/events/[event_key]/EventMatchesTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* EventMatchesTab
*
* Matches tab for `/events/[event_key]`. Defaults to `loading = true`
* (skeleton) since there's no real data source wired up yet — once
* the TBA matches endpoint is adapted into `EventMatch[]`
* (`src/types/events.ts`), pass it in:
*
* <EventMatchesTab matches={event.matches} loading={false} />
*/
import { Swords } from "lucide-react";
import { EventMatch } from "@/types/events";
import MatchesList from "@/components/events/event/matches/MatchesList";
import EmptyTabState from "./EmptyTabState";

interface EventMatchesTabProps {
matches?: EventMatch[];
loading?: boolean;
}

export default function EventMatchesTab({ matches = [], loading = true }: EventMatchesTabProps) {
const isEmpty = !loading && matches.length === 0;

return (
<div className="space-y-6">
{(loading || isEmpty) && (
<EmptyTabState
icon={Swords}
title={loading ? "Match schedule not available yet" : "No matches yet"}
description={
loading
? "Qualification and playoff matches will appear here once the event publishes its schedule."
: "This event doesn't have any scheduled matches yet."
}
/>
)}

<section className="rounded-2xl border border-slate-800 bg-slate-900 p-6">
<div className="flex items-center justify-between gap-3">
<h2 className="text-lg font-semibold text-white">
Matches
</h2>

<div className="flex gap-2">
<span className="rounded-lg bg-blue-600/20 px-3 py-1.5 text-xs font-medium text-blue-400">
Qualification
</span>

<span className="rounded-lg px-3 py-1.5 text-xs font-medium text-slate-500">
Playoffs
</span>
</div>
</div>

{!isEmpty && (
<div className="mt-5">
<MatchesList matches={matches} loading={loading} />
</div>
)}
</section>
</div>
);
}
53 changes: 53 additions & 0 deletions src/app/events/[event_key]/EventRankingsTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* EventRankingsTab
*
* Rankings tab for `/events/[event_key]`. Defaults to `loading = true`
* (skeleton) since there's no real data source wired up yet — once
* the TBA rankings endpoint is adapted into `EventRankingRow[]`
* (`src/types/events.ts`), pass it in:
*
* <EventRankingsTab rankings={event.rankings} loading={false} />
*/
import { ListOrdered } from "lucide-react";
import { EventRankingRow } from "@/types/events";
import RankingsTable from "@/components/events/event/rankings/RankingsTable";
import EmptyTabState from "./EmptyTabState";

interface EventRankingsTabProps {
rankings?: EventRankingRow[];
loading?: boolean;
}

export default function EventRankingsTab({ rankings = [], loading = true }: EventRankingsTabProps) {
const isEmpty = !loading && rankings.length === 0;

return (
<div className="space-y-6">
{(loading || isEmpty) && (
<EmptyTabState
icon={ListOrdered}
title={loading ? "Rankings not available yet" : "No rankings yet"}
description={
loading
? "Qualification rankings will populate here once matches have been played."
: "This event doesn't have qualification rankings yet."
}
/>
)}

<section className="overflow-hidden rounded-2xl border border-slate-800 bg-slate-900">
<div className="p-6 pb-0">
<h2 className="text-lg font-semibold text-white">
Rankings
</h2>
</div>

{!isEmpty && (
<div className="mt-5">
<RankingsTable rankings={rankings} loading={loading} />
</div>
)}
</section>
</div>
);
}
51 changes: 51 additions & 0 deletions src/app/events/[event_key]/EventTeamsTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* EventTeamsTab
*
* Teams tab for `/events/[event_key]`. Defaults to `loading = true`
* (skeleton) since there's no real data source wired up yet — once
* the TBA teams endpoint is adapted into `EventTeamSummary[]`
* (`src/types/events.ts`), pass it in:
*
* <EventTeamsTab teams={event.teams} loading={false} />
*/
import { Users } from "lucide-react";
import { EventTeamSummary } from "@/types/events";
import TeamsGrid from "@/components/events/event/teams/TeamsGrid";
import EmptyTabState from "./EmptyTabState";

interface EventTeamsTabProps {
teams?: EventTeamSummary[];
loading?: boolean;
}

export default function EventTeamsTab({ teams = [], loading = true }: EventTeamsTabProps) {
const isEmpty = !loading && teams.length === 0;

return (
<div className="space-y-6">
{(loading || isEmpty) && (
<EmptyTabState
icon={Users}
title={loading ? "Team list not available yet" : "No teams listed"}
description={
loading
? "The full roster of participating teams will show up here once the event data syncs."
: "This event doesn't have any teams registered yet."
}
/>
)}

<section className="rounded-2xl border border-slate-800 bg-slate-900 p-6">
<h2 className="text-lg font-semibold text-white">
Teams
</h2>

{!isEmpty && (
<div className="mt-5">
<TeamsGrid teams={teams} loading={loading} />
</div>
)}
</section>
</div>
);
}
Loading
Loading