games: per-game pages, games.library table, games group in header/tiles/start
All checks were successful
Deploy production / deploy (push) Successful in 1m7s
All checks were successful
Deploy production / deploy (push) Successful in 1m7s
This commit is contained in:
51
src/lib/server/db/schema/game-library.ts
Normal file
51
src/lib/server/db/schema/game-library.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { check, integer, text } from 'drizzle-orm/pg-core';
|
||||
import { gamesSchema } from './schemas';
|
||||
import { id, timestamps } from './helpers';
|
||||
|
||||
// Every game I play (plus speedcubing): one row per game, shown at /trackers/games/<slug>.
|
||||
// The header, home, /trackers and /start chips come from `gameTrackers` in #lib/projects
|
||||
// (static, so navigation needs no query); keep the slugs in sync with these rows.
|
||||
// Deep trackers (osu! journal, Call of Duty mastery, Pokémon catches) stay where they are
|
||||
// and are linked through `tracker_href`.
|
||||
export const GAME_STATUSES = ['main', 'active', 'occasional', 'unknown'] as const;
|
||||
export const GAME_KINDS = ['video', 'puzzle'] as const;
|
||||
|
||||
export const gameLibrary = gamesSchema.table(
|
||||
'library',
|
||||
{
|
||||
id: id(),
|
||||
slug: text('slug').notNull().unique(),
|
||||
name: text('name').notNull(),
|
||||
shortName: text('short_name').notNull(),
|
||||
kind: text('kind', { enum: GAME_KINDS }).notNull().default('video'),
|
||||
status: text('status', { enum: GAME_STATUSES }).notNull().default('unknown'),
|
||||
/** Where I play it (PC, Nintendo, physical…); null when not known yet. */
|
||||
platform: text('platform'),
|
||||
/** Internal path of a deeper tracker for this game, if there is one. */
|
||||
trackerHref: text('tracker_href'),
|
||||
/** Rank as a position (#40 000 is 40000); only values I gave. */
|
||||
currentRank: integer('current_rank'),
|
||||
peakRank: integer('peak_rank'),
|
||||
/** What the rank numbers mean, e.g. "osu! standard global rank". */
|
||||
rankLabel: text('rank_label'),
|
||||
/** Names this game goes by elsewhere (hardware.sensitivities.game, old titles). */
|
||||
aliases: text('aliases')
|
||||
.array()
|
||||
.notNull()
|
||||
.default(sql`'{}'::text[]`),
|
||||
notes: text('notes'),
|
||||
sortOrder: integer('sort_order').notNull().default(100),
|
||||
...timestamps
|
||||
},
|
||||
(t) => [
|
||||
check('library_status_check', sql`${t.status} IN ('main','active','occasional','unknown')`),
|
||||
check('library_kind_check', sql`${t.kind} IN ('video','puzzle')`),
|
||||
check(
|
||||
'library_rank_check',
|
||||
sql`(${t.currentRank} IS NULL OR ${t.currentRank} > 0) AND (${t.peakRank} IS NULL OR ${t.peakRank} > 0)`
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
export type GameLibraryRow = typeof gameLibrary.$inferSelect;
|
||||
@@ -5,6 +5,7 @@ export * from './blog';
|
||||
export * from './progress';
|
||||
export * from './catch-em-all';
|
||||
export * from './game-tracker';
|
||||
export * from './game-library';
|
||||
export * from './tracker-journal';
|
||||
export * from './animaldex';
|
||||
export * from './lists'; // meta registry; the lists themselves are runtime tables
|
||||
|
||||
Reference in New Issue
Block a user