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
11 changes: 5 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This is a Create React App (React 18) single-page application styled with Tailwi
src/
components/ # Shared UI — imported by multiple pages
AccountTable.tsx # Account page's per-alt table (ilvl, M+ rating, professions as columns)
AltTable.tsx # Table used by Gear, Profession (Account no longer uses this — see below)
AltTable.tsx # Table used by Gear only (Account no longer uses this — see below)
CollapsePanel.tsx # Animated expand/collapse wrapper
Header.tsx # Top nav bar: brand + MenuBar nav links + Update button + staleness banner
KeybindUpload.tsx # .lua addon file upload form (despite the name, generic upload widget)
Expand All @@ -31,7 +31,7 @@ src/
PetTable.tsx # Pet icon-grid accordion (collected + toggle for uncollected)
ProfessionTable.tsx # Profession recipe accordion
pages/ # One file per route
Account.tsx # "/" — alt-centric accordion list; click an alt name for AltDetail
Account.tsx # "/" — alt-centric table (AccountTable); click an alt name for AltDetail
Achievement.tsx # Account-wide; grouped by category; lazy-loads per-category on expand
AltDetail.tsx # "/alt/:alt/:realm" — consolidated per-alt view (gear/professions/M+/achievements/reps)
Auth.tsx
Expand All @@ -41,10 +41,9 @@ src/
Mount.tsx
MythicPlus.tsx # Account-wide; accordion per alt sorted by rating
Pet.tsx
Profession.tsx # Account-wide profession overview table
Reputation.tsx # Account-wide; grouped by alt → expansion; standing derived from raw value
SingleGear.tsx # Click-through target from Gear.tsx (NOT from AltDetail, which duplicates this rendering)
SingleProfession.tsx # Click-through target from Profession.tsx (same note as SingleGear)
SingleProfession.tsx # Click-through target from AccountTable's Profession 1/2 columns (AltDetail renders ProfessionTable inline instead, no navigation)
App.tsx # BrowserRouter + Routes only
App.css # WoW-specific styles (class colors, item quality borders)
classColors.ts # WoW class → hex color map, shared by AltTable and AccountTable
Expand Down Expand Up @@ -82,9 +81,9 @@ Absolute imports are configured via `baseUrl: "src"` in `tsconfig.json`, so impo

**Page layout pattern**: every page uses `<PageLayout title="...">` which renders `<Header />` (a single top nav bar — brand, `<MenuBar />` nav links, and Update controls all in one row, modelled on a sibling project's `renegades-bot/web/templates/base.html`) + a centered `max-w-7xl` main content area. `MenuBar` conditionally shows authenticated links based on whether the `userid` cookie exists, and underlines the active tab via `useLocation()`. `AltDetail` has no nav link — it's only reachable by clicking an alt name on the Account page.

**`AltTable`**: shared table used by Gear and Profession (the account-wide overview pages). Per-page rendering is controlled by a `page` prop (`'gear'`, `'profession'`) which governs which columns are hidden and which cells link to detail views. The Account page does **not** use this anymore — it has its own table component (`AccountTable`), styled separately since it needs extra columns (ilvl, M+ rating, professions) that `AltTable`'s `page` prop branching doesn't support.
**`AltTable`**: shared table used only by `Gear.tsx` now (the standalone Profession page was removed — see below). The `page` prop (`PageType`, currently just `'gear'`) is now a single-branch vestige of when it also handled `'profession'`; not worth collapsing further unless another standalone page needs the pattern again.

**Account page redesign (alts as the focal point)**: `Account.tsx` fetches `/api/profile/alts/`, `/api/profile/altmythicplus/`, and `/api/profile/altprofessions/` in parallel and joins them by `alt_id` into a per-alt row (ilvl, M+ rating, professions) rendered via `AccountTable`. Achievement points and reputation are deliberately left off this table — achievements are scraped from one representative alt per faction (not every alt, see `scan_user_collection` in the backend), so they aren't meaningfully per-alt data; reputation was a judgement call to keep the table focused. Clicking an alt's name navigates to `AltDetail` (gear/professions/M+/reputations for that one alt — also no achievements section, same reasoning). The standalone Gear/Profession/Achievement/Reputation/Mythic+ pages are intentionally untouched by this — they're a known-incomplete first step of a larger planned redesign, not yet decided whether they'll be folded in or stay as account-wide views.
**Account page redesign (alts as the focal point)**: `Account.tsx` fetches `/api/profile/alts/`, `/api/profile/altmythicplus/`, and `/api/profile/altprofessions/` in parallel and joins them by `alt_id` into a per-alt row (ilvl, M+ rating, professions) rendered via `AccountTable`. Rows are sorted client-side by level descending, then ilvl descending. The Profession 1/2 cells link to `SingleProfession` (`/profession/:alt/:realm/:profession`) when learned. Achievement points and reputation are deliberately left off this table — achievements are scraped from one representative alt per faction (not every alt, see `scan_user_collection` in the backend), so they aren't meaningfully per-alt data; reputation was a judgement call to keep the table focused. Clicking an alt's name navigates to `AltDetail` (gear/professions/M+/reputations for that one alt — also no achievements section, same reasoning). The standalone Profession page was removed entirely (decided unnecessary now that professions are clickable from Account) — Gear/Achievement/Reputation/Mythic+ remain, status still undecided.

**Data slicing in Mount/Pet**: the API appends count metadata at the end of the response array. `data.slice(0, -3)` passes actual records to the table; `data.slice(-3)` retrieves the counts.

Expand Down
2 changes: 0 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import Auth from 'pages/Auth';
import AuthRedirect from 'pages/AuthRedirect';
import Account from 'pages/Account';
import Gear from 'pages/Gear';
import Profession from 'pages/Profession';
import Mount from 'pages/Mount';
import Pet from 'pages/Pet';
import Achievement from 'pages/Achievement';
Expand All @@ -24,7 +23,6 @@ function App() {
<Route path="/auth" element={<Auth />} />
<Route path="/redirect" element={<AuthRedirect />} />
<Route path="/gear" element={<Gear />} />
<Route path="/profession" element={<Profession />} />
<Route path="/mount" element={<Mount />} />
<Route path="/pet" element={<Pet />} />
<Route path="/achievement" element={<Achievement />} />
Expand Down
43 changes: 36 additions & 7 deletions src/components/AccountTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,52 @@ export interface AccountAlt {
const thBase =
'bg-zinc-800 text-zinc-300 px-4 py-2 border border-zinc-700 text-sm font-semibold text-left';
const tdBase = 'px-4 py-2 border border-zinc-700 text-sm';
const profLinkCls = 'text-amber-400 hover:text-amber-300 hover:underline underline-offset-2';

interface ProfessionCellProps {
altName: string;
realmSlug: string;
profession: string;
}

function ProfessionCell({ altName, realmSlug, profession }: ProfessionCellProps) {
if (profession === 'Missing') {
return <td className={`${tdBase} text-zinc-600`}>{profession}</td>;
}
return (
<td className={tdBase}>
<Link
to={`/profession/${altName.toLowerCase()}/${realmSlug}/${profession.toLowerCase()}`}
className={profLinkCls}
>
{profession}
</Link>
</td>
);
}

interface AccountTableRowProps {
alt: AccountAlt;
mythicRating: number | null;
professions: [string, string] | null;
rowNumber: number;
index: number;
}

function AccountTableRow({ alt, mythicRating, professions, index }: AccountTableRowProps) {
function AccountTableRow({
alt,
mythicRating,
professions,
rowNumber,
index,
}: AccountTableRowProps) {
const color = classColor(alt.className) ?? '#e4e4e7';
const bg = index % 2 === 0 ? '#18181b' : '#27272a';
const [prof1, prof2] = professions ?? ['Missing', 'Missing'];

return (
<tr style={{ backgroundColor: bg }} className="hover:brightness-125 transition-all">
<td className={`${tdBase} text-zinc-500 text-center`}>{rowNumber}</td>
<td className={tdBase}>
<Link
to={`/alt/${alt.name.toLowerCase()}/${alt.realmSlug}`}
Expand All @@ -49,12 +80,8 @@ function AccountTableRow({ alt, mythicRating, professions, index }: AccountTable
<td className={`${tdBase} text-zinc-300 text-center`}>
{mythicRating !== null ? mythicRating.toFixed(1) : '—'}
</td>
<td className={`${tdBase} ${prof1 === 'Missing' ? 'text-zinc-600' : 'text-zinc-300'}`}>
{prof1}
</td>
<td className={`${tdBase} ${prof2 === 'Missing' ? 'text-zinc-600' : 'text-zinc-300'}`}>
{prof2}
</td>
<ProfessionCell altName={alt.name} realmSlug={alt.realmSlug} profession={prof1} />
<ProfessionCell altName={alt.name} realmSlug={alt.realmSlug} profession={prof2} />
</tr>
);
}
Expand All @@ -71,6 +98,7 @@ function AccountTable({ alts, mythicByAlt, professionsByAlt }: AccountTableProps
<table className="border-collapse text-sm w-full">
<thead>
<tr>
<th className={`${thBase} text-center`}>#</th>
<th className={thBase}>Name</th>
<th className={thBase}>Realm</th>
<th className={thBase}>Level</th>
Expand All @@ -89,6 +117,7 @@ function AccountTable({ alts, mythicByAlt, professionsByAlt }: AccountTableProps
alt={alt}
mythicRating={mythicByAlt.get(alt.altId) ?? null}
professions={professionsByAlt.get(alt.altId) ?? null}
rowNumber={index + 1}
index={index}
/>
))}
Expand Down
32 changes: 0 additions & 32 deletions src/components/AltTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,6 @@ interface AltTableRowDataProps {
}

function AltTableRowData({ alt, fullalt, page }: AltTableRowDataProps) {
if (page === 'profession') {
if (alt === fullalt[2]) return null;
if (alt === fullalt[3] && fullalt[3] !== 'Missing') {
return (
<td className={tdBase}>
<Link
className={linkCls}
to={`/profession/${String(fullalt[0]).toLowerCase()}/${String(fullalt[1]).toLowerCase().replace("'", '')}/${String(fullalt[3]).toLowerCase()}`}
>
{fullalt[3]}
</Link>
</td>
);
}
if (alt === fullalt[4] && fullalt[4] !== 'Missing') {
return (
<td className={tdBase}>
<Link
className={linkCls}
to={`/profession/${String(fullalt[0]).toLowerCase()}/${String(fullalt[1]).toLowerCase().replace("'", '')}/${String(fullalt[4]).toLowerCase()}`}
>
{fullalt[4]}
</Link>
</td>
);
}
if (alt === 'Missing') {
return <td className={`${tdBase} text-zinc-500`}>{alt}</td>;
}
return <td className={tdBase}>{alt}</td>;
}

if (page === 'gear') {
if (alt === fullalt[2]) return null;
for (const item of fullalt.slice(4)) {
Expand Down
1 change: 0 additions & 1 deletion src/components/MenuBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ function LoginLogout() {
<>
<NavLink to="/">Account</NavLink>
<NavLink to="/gear">Gear</NavLink>
<NavLink to="/profession">Profession</NavLink>
<NavLink to="/mount">Mount</NavLink>
<NavLink to="/pet">Pet</NavLink>
<NavLink to="/achievement">Achievements</NavLink>
Expand Down
5 changes: 4 additions & 1 deletion src/pages/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ function Account() {
}),
]);

setAlts((altsRes.data as RawAltRow[]).map(parseAlt));
const sortedAlts = (altsRes.data as RawAltRow[])
.map(parseAlt)
.sort((a, b) => b.level - a.level || b.ilvl - a.ilvl);
setAlts(sortedAlts);
setMythicByAlt(
new Map((mythicRes.data as MythicPlusEntry[]).map((m) => [m.alt, m.mythic_rating]))
);
Expand Down
51 changes: 0 additions & 51 deletions src/pages/Profession.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type PageType = 'gear' | 'profession';
export type PageType = 'gear';

export type AltRow = (string | number)[];

Expand Down
Loading