games: per-game pages, games.library table, games group in header/tiles/start
All checks were successful
Deploy production / deploy (push) Successful in 1m7s

This commit is contained in:
2026-10-25 15:20:45 +00:00
parent 037a525f91
commit d79b73ad83
19 changed files with 27712 additions and 38 deletions

View File

@@ -0,0 +1,54 @@
CREATE TABLE "games"."library" (
"id" serial PRIMARY KEY,
"slug" text NOT NULL UNIQUE,
"name" text NOT NULL,
"short_name" text NOT NULL,
"kind" text DEFAULT 'video' NOT NULL,
"status" text DEFAULT 'unknown' NOT NULL,
"platform" text,
"tracker_href" text,
"current_rank" integer,
"peak_rank" integer,
"rank_label" text,
"aliases" text[] DEFAULT '{}'::text[] NOT NULL,
"notes" text,
"sort_order" integer DEFAULT 100 NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "library_status_check" CHECK ("status" IN ('main','active','occasional','unknown')),
CONSTRAINT "library_kind_check" CHECK ("kind" IN ('video','puzzle')),
CONSTRAINT "library_rank_check" CHECK (("current_rank" IS NULL OR "current_rank" > 0) AND ("peak_rank" IS NULL OR "peak_rank" > 0))
);
--> statement-breakpoint
-- Hand-written: grants (same pattern as the other app tables) and the initial games.
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_app') THEN
GRANT SELECT, INSERT, UPDATE, DELETE ON games.library TO personal_app;
GRANT USAGE, SELECT ON SEQUENCE games.library_id_seq TO personal_app;
END IF;
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_ro') THEN
GRANT SELECT ON games.library TO personal_ro;
END IF;
END $$;
--> statement-breakpoint
-- Ranks only where Stan gave them (osu!: ~#40k now, peak ~#11k); platform null = not known.
INSERT INTO games.library (slug, name, short_name, kind, status, platform, tracker_href, current_rank, peak_rank, rank_label, aliases, sort_order) VALUES
('osu', 'osu!', 'osu!', 'video', 'main', 'PC', '/projects/game-tracker/osu', 40000, 11000, 'osu! global rank, approximate', '{}', 10),
('cs2', 'Counter-Strike 2', 'CS2', 'video', 'active', 'PC', NULL, NULL, NULL, NULL, '{CS:GO,"Counter-Strike: Global Offensive"}', 20),
('pubg', 'PUBG: Battlegrounds', 'PUBG', 'video', 'active', NULL, NULL, NULL, NULL, NULL, '{PUBG,"PlayerUnknown''s Battlegrounds"}', 30),
('r6', 'Tom Clancy''s Rainbow Six Siege', 'R6 Siege', 'video', 'active', NULL, NULL, NULL, NULL, NULL, '{"Rainbow Six Siege",R6}', 40),
('tarkov', 'Escape from Tarkov', 'Tarkov', 'video', 'active', 'PC', NULL, NULL, NULL, NULL, '{EFT}', 50),
('bo2', 'Call of Duty: Black Ops II', 'Black Ops II', 'video', 'active', NULL, '/projects/game-tracker/call-of-duty/black-ops-2', NULL, NULL, NULL, '{"Call of Duty",BO2,"Black Ops 2"}', 60),
('valorant', 'Valorant', 'Valorant', 'video', 'occasional', NULL, NULL, NULL, NULL, NULL, '{}', 70),
('lol', 'League of Legends', 'League', 'video', 'occasional', 'PC', NULL, NULL, NULL, NULL, '{LoL}', 80),
('overwatch', 'Overwatch 2', 'Overwatch 2', 'video', 'occasional', NULL, NULL, NULL, NULL, NULL, '{Overwatch}', 90),
('minecraft', 'Minecraft', 'Minecraft', 'video', 'occasional', NULL, NULL, NULL, NULL, NULL, '{}', 100),
('factorio', 'Factorio', 'Factorio', 'video', 'occasional', NULL, NULL, NULL, NULL, NULL, '{}', 110),
('ksp', 'Kerbal Space Program', 'KSP', 'video', 'occasional', NULL, NULL, NULL, NULL, NULL, '{}', 120),
('smash', 'Super Smash Bros.', 'Smash Bros', 'video', 'occasional', 'Nintendo', NULL, NULL, NULL, NULL, '{"Super Smash Bros"}', 130),
('cubing', 'Speedcubing', 'speedcubing', 'puzzle', 'occasional', 'physical', NULL, NULL, NULL, NULL, '{"Rubik''s Cube"}', 140),
('pokemon', 'Pokémon', 'Pokémon', 'video', 'unknown', NULL, '/projects/game-tracker/pokemon', NULL, NULL, NULL, '{}', 150),
('sot', 'Sea of Thieves', 'Sea of Thieves', 'video', 'unknown', NULL, NULL, NULL, NULL, NULL, '{}', 160)
ON CONFLICT (slug) DO NOTHING;