From e1a6e4e38d4a59e9e23bdc12ae6aff121bd29416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:11:31 -0500 Subject: [PATCH] feat: spotify like table --- packages/i18n/strings/en.json | 5 +- .../src/components/CurrentlyPlaying.tsx | 7 ++ .../website/src/components/PlaylistOrder.tsx | 44 +++++------- .../website/src/components/PlaylistTable.tsx | 55 ++++++++++++++ .../src/components/PlaylistTankEntry.tsx | 72 ++++++++----------- .../src/components/StickyRowHeaderCell.tsx | 11 ++- .../src/components/TankRowHeaderCell.tsx | 51 +++++++------ .../src/core/blitzkit/generatePlaylist.ts | 2 + packages/website/src/layouts/ToolLayout.astro | 6 +- .../src/pages/[...locale]/playlist/_index.tsx | 2 +- .../pages/[...locale]/playlist/index.astro | 2 +- packages/website/src/stores/playlist.ts | 16 +++-- 12 files changed, 159 insertions(+), 114 deletions(-) create mode 100644 packages/website/src/components/CurrentlyPlaying.tsx create mode 100644 packages/website/src/components/PlaylistTable.tsx diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 515686ada..1b7b3c9d3 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -862,7 +862,10 @@ "playlist": { "clear": "Clear", "table": { - "tank": "Tank" + "index": "#", + "never": "Never", + "tank": "Tank", + "last_played": "Last played" }, "no_login_warning": "You are not logged in. BlitzKit will not keep track of your played tanks.", "generate": "Generate playlist", diff --git a/packages/website/src/components/CurrentlyPlaying.tsx b/packages/website/src/components/CurrentlyPlaying.tsx new file mode 100644 index 000000000..08427e3f1 --- /dev/null +++ b/packages/website/src/components/CurrentlyPlaying.tsx @@ -0,0 +1,7 @@ +import { Flex } from "@radix-ui/themes"; + +export function CurrentlyPlaying() { + return + + ; +} diff --git a/packages/website/src/components/PlaylistOrder.tsx b/packages/website/src/components/PlaylistOrder.tsx index c73238131..514e9d206 100644 --- a/packages/website/src/components/PlaylistOrder.tsx +++ b/packages/website/src/components/PlaylistOrder.tsx @@ -1,16 +1,23 @@ -import { Button, Flex, Table } from "@radix-ui/themes"; +import { Box, Button, Flex } from "@radix-ui/themes"; import { useLocale } from "../hooks/useLocale"; import { Playlist } from "../stores/playlist"; -import { PlaylistTankEntry } from "./PlaylistTankEntry"; +import { CurrentlyPlaying } from "./CurrentlyPlaying"; +import { PlaylistTable } from "./PlaylistTable"; export function PlaylistOrder() { const { strings } = useLocale(); - const list = Playlist.use((state) => state.list); - - if (!list) return null; return ( - + + + - - - - - - {strings.website.tools.playlist.table.tank} - - - - - - - {list.map(({ tank, checked }, index) => ( - - ))} - - + + + ); } diff --git a/packages/website/src/components/PlaylistTable.tsx b/packages/website/src/components/PlaylistTable.tsx new file mode 100644 index 000000000..92ed64efb --- /dev/null +++ b/packages/website/src/components/PlaylistTable.tsx @@ -0,0 +1,55 @@ +import { Table, Text } from "@radix-ui/themes"; +import { useLocale } from "../hooks/useLocale"; +import { Playlist } from "../stores/playlist"; +import { PlaylistTankEntry } from "./PlaylistTankEntry"; +import { StickyColumnHeaderCell } from "./StickyColumnHeaderCell"; +import { StickyTableRoot } from "./StickyTableRoot"; + +export function PlaylistTable() { + const { strings } = useLocale(); + const list = Playlist.use((state) => state.list); + + if (!list) return null; + + return ( + + + + + + {strings.website.tools.playlist.table.index} + + + + + {strings.website.tools.playlist.table.tank} + + + + {strings.website.tools.playlist.table.last_played} + + + + + + + + + + {list.map((props, index) => ( + + ))} + + + ); +} diff --git a/packages/website/src/components/PlaylistTankEntry.tsx b/packages/website/src/components/PlaylistTankEntry.tsx index 1e4c2a517..923adba00 100644 --- a/packages/website/src/components/PlaylistTankEntry.tsx +++ b/packages/website/src/components/PlaylistTankEntry.tsx @@ -1,18 +1,31 @@ -import type { TankDefinition } from "@blitzkit/core"; -import { DragHandleDots2Icon, MinusCircledIcon } from "@radix-ui/react-icons"; -import { Checkbox, Flex, IconButton, Table, Text } from "@radix-ui/themes"; -import { Playlist } from "../stores/playlist"; +import { MinusCircledIcon } from "@radix-ui/react-icons"; +import { Checkbox, IconButton, Table } from "@radix-ui/themes"; +import { useLocale } from "../hooks/useLocale"; +import { Playlist, type PlaylistEntry } from "../stores/playlist"; import { TankRowHeaderCell } from "./TankRowHeaderCell"; -interface Props { - tank: TankDefinition; - checked: boolean; +interface Props extends PlaylistEntry { index: number; } -export function PlaylistTankEntry({ tank, checked, index }: Props) { +export function PlaylistTankEntry({ tank, checked, last, index }: Props) { + const { locale, strings } = useLocale(); + const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" }); + const nowDays = Date.now() / (1000 * 60 * 60 * 24); + const lastDays = last ? last / (60 * 60 * 24) : undefined; + return ( + {index + 1} + + + + + {lastDays + ? rtf.format(Math.floor(lastDays - nowDays), "day") + : strings.website.tools.playlist.table.never} + + { Playlist.mutate((draft) => { @@ -23,41 +36,18 @@ export function PlaylistTankEntry({ tank, checked, index }: Props) { - - - { + Playlist.mutate((draft) => { + draft.list!.splice(index, 1); + }); + }} > - { - Playlist.mutate((draft) => { - draft.list!.splice(index, 1); - }); - }} - > - - - - - - - - - + + ); diff --git a/packages/website/src/components/StickyRowHeaderCell.tsx b/packages/website/src/components/StickyRowHeaderCell.tsx index 17c14fd38..8e0957278 100644 --- a/packages/website/src/components/StickyRowHeaderCell.tsx +++ b/packages/website/src/components/StickyRowHeaderCell.tsx @@ -1,15 +1,14 @@ -import { Table } from '@radix-ui/themes'; -import type { RowHeaderCellProps } from '@radix-ui/themes/src/components/table.js'; +import { Table } from "@radix-ui/themes"; +import type { RowHeaderCellProps } from "@radix-ui/themes/src/components/table.js"; export function StickyRowHeaderCell({ style, ...props }: RowHeaderCellProps) { return ( + {unwrap(tank.name)} @@ -36,34 +33,34 @@ export function TankRowHeaderCell({ tank }: TankRowHeaderCellProps) { locale={locale} color={ tank.type === TankType.COLLECTOR - ? 'blue' + ? "blue" : tank.type === TankType.PREMIUM - ? 'amber' - : 'gray' + ? "amber" + : "gray" } highContrast={tank.type === TankType.RESEARCHABLE} underline="hover" wrap="nowrap" href={`/tanks/${tank.slug}`} style={{ - paddingLeft: 'var(--space-2)', - display: 'flex', - alignItems: 'center', - gap: 'var(--space-1)', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', + paddingLeft: "var(--space-2)", + display: "flex", + alignItems: "center", + gap: "var(--space-1)", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", }} - size={{ initial: '1', sm: '2' }} + size={{ initial: "1", sm: "2" }} > {tank.testing && ( - + )} {unwrap(tank.name)} diff --git a/packages/website/src/core/blitzkit/generatePlaylist.ts b/packages/website/src/core/blitzkit/generatePlaylist.ts index 0a3ddf209..717226b6c 100644 --- a/packages/website/src/core/blitzkit/generatePlaylist.ts +++ b/packages/website/src/core/blitzkit/generatePlaylist.ts @@ -35,6 +35,8 @@ export async function generatePlaylist() { tank, battles: stats[tank.id]?.all.battles, wins: stats[tank.id]?.all.wins, + last: stats[tank.id]?.last_battle_time, + checked: false, })); }); } diff --git a/packages/website/src/layouts/ToolLayout.astro b/packages/website/src/layouts/ToolLayout.astro index 348fec199..be9997afe 100644 --- a/packages/website/src/layouts/ToolLayout.astro +++ b/packages/website/src/layouts/ToolLayout.astro @@ -8,9 +8,11 @@ import BaseLayout from "./BaseLayout.astro"; interface Props { tool: Tool; locale: string; + noNavbar?: boolean; + noFooter?: boolean; } -const { tool, locale } = Astro.props; +const { tool, locale, noNavbar, noFooter } = Astro.props; const strings = getStrings(locale); const toolStrings = strings.website.tools[ @@ -20,7 +22,7 @@ const toolStrings = - + - + {!list && } {list && } diff --git a/packages/website/src/pages/[...locale]/playlist/index.astro b/packages/website/src/pages/[...locale]/playlist/index.astro index 33cce4318..298a243d7 100644 --- a/packages/website/src/pages/[...locale]/playlist/index.astro +++ b/packages/website/src/pages/[...locale]/playlist/index.astro @@ -8,7 +8,7 @@ export { getStaticPaths } from "../index.astro"; const { locale } = Astro.params; --- - + diff --git a/packages/website/src/stores/playlist.ts b/packages/website/src/stores/playlist.ts index 387545808..b0fcb496a 100644 --- a/packages/website/src/stores/playlist.ts +++ b/packages/website/src/stores/playlist.ts @@ -1,14 +1,16 @@ import type { TankDefinition } from "@blitzkit/core"; import { Varuna } from "varuna"; +export interface PlaylistEntry { + tank: TankDefinition; + battles?: number; + wins?: number; + checked: boolean; + last?: number; +} + export interface Playlist { - list?: { - index: number; - tank: TankDefinition; - battles?: number; - wins?: number; - checked: boolean; - }[]; + list?: PlaylistEntry[]; } export const Playlist = new Varuna({}, "playlist");