Skip to content

Commit 3898ba9

Browse files
committed
feat(add): game references hub
1 parent d136fd4 commit 3898ba9

30 files changed

Lines changed: 3332 additions & 840 deletions

File tree

CHANGELOG.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,49 @@ All notable changes to FixFX will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.3.0] - 2026-03-04
9+
10+
### Added
11+
12+
#### Game References
13+
14+
- **Game References section** (`/game-references`) — 12 new reference pages backed by the `/api/game-references` endpoints, all with search, pagination, and loading skeletons
15+
- **Blips** (`/game-references/blips`) — Image grid of all minimap blip icons with ID labels; secondary tab for the blip color palette with hex swatches
16+
- **Checkpoints** (`/game-references/checkpoints`) — Image grid of all checkpoint types with section filter tabs (standard 0–49 / type 44–46 variant)
17+
- **Markers** (`/game-references/markers`) — Image grid of all 44 `DRAW_MARKER` types with ID and name labels
18+
- **Ped Models** (`/game-references/ped-models`) — Image grid of all pedestrian models with category filter chips, prop/component counts, and pagination
19+
- **Weapon Models** (`/game-references/weapon-models`) — Card grid grouped by weapon type with expandable detail panels (hash key, model hash key, DLC, description, components, tints)
20+
- **Data Files** (`/game-references/data-files`) — Searchable table of all resource manifest `data_file` keys with file type, root element, mounter, and example columns
21+
- **Game Events** (`/game-references/game-events`) — Searchable table of client-side game events with descriptions
22+
- **Gamer Tags** (`/game-references/gamer-tags`) — Table of head display component IDs and names
23+
- **HUD Colors** (`/game-references/hud-colors`) — Toggle between a color swatch grid and a full RGBA/hex table for all ~234 HUD color indices
24+
- **Net Game Events** (`/game-references/net-game-events`) — Searchable table of `GTA_EVENT_IDS` enum entries with sequential IDs
25+
- **Pickup Hashes** (`/game-references/pickup-hashes`) — Searchable table of `ePickupHashes` enum entries with numeric hash values
26+
- **Zones** (`/game-references/zones`) — Searchable table of all 1300+ map zones with zone name ID, zone name, and description
27+
- **Game References Hub** (`/game-references`) — Landing page with a hero section, live stats row (category count + total entries from `/api/game-references/summary`), and a 4-column responsive card grid; each card displays a per-category entry count badge fetched from the summary endpoint
28+
- **Game References Layout** — Shared SEO metadata and Open Graph tags for the `/game-references` route group
29+
30+
### Changed
31+
32+
#### JSON Validator
33+
34+
- **Modular Architecture** — Rewrote the monolithic 913-line `validator-content.tsx` into a fully modular plugin-based system
35+
- `types.ts` — Shared TypeScript interfaces (`ValidatorType`, `IssueSeverity`, `ValidationIssue`, `ValidationResult`, `ValidatorConfig`, `ValidatorPlaceholder`)
36+
- `base-validator.ts` — Abstract `BaseValidator` class with shared `parseJson`, `formatJson`, `createIssue`, and `getConfig` methods
37+
- `validators/generic.ts``GenericJsonValidator` for plain JSON syntax validation
38+
- `validators/txadmin-embed.ts``TxAdminEmbedValidator` with Discord embed structure and character limit enforcement
39+
- `validators/txadmin-config.ts``TxAdminConfigValidator` with hex color, button structure, and status string/color pairing validation
40+
- `registry.ts``ValidatorRegistry` singleton for registering, retrieving, and querying all validators
41+
- **Componentized UI** — Split rendering into focused, reusable components under `core/validator/components/`
42+
- `ValidatorHeader` — Title, mode badge, and Format / Clear / Validate action buttons with keyboard shortcut hints
43+
- `EditorPanel` — JSON input textarea with invalid-state styling and `Ctrl+Enter` hint
44+
- `ResultsPanel` — Animated results display with severity badges, issue path/message/suggestion rendering, formatted output copy, and validation metadata footer (character count, line count, validation time)
45+
- `ValidatorSidebar` — Collapsible sidebar (expanded `w-72` / collapsed `w-20`) with validation mode selector, quick templates, click-to-insert placeholder variables, and resource links
46+
- **Validation Metadata** — Results now include `validationTime`, `characterCount`, and `lineCount` surfaced in the results panel footer
47+
- **Type rename** — Internal validator type `txadmin-embed-config` renamed to `txadmin-config` for consistency
48+
49+
---
50+
851
## [1.2.0] - 2026-02-14
952

1053
### Added
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
"use client";
2+
3+
import Link from "next/link";
4+
import { ChevronLeft, Search, X, type LucideIcon } from "lucide-react";
5+
import { Button } from "@ui/components/button";
6+
import { Input } from "@ui/components/input";
7+
import { cn } from "@utils/functions/cn";
8+
9+
// ---------------------------------------------------------------------------
10+
// PageShell
11+
// ---------------------------------------------------------------------------
12+
13+
interface GameReferencePageShellProps {
14+
icon: LucideIcon;
15+
iconColor: string;
16+
iconBg: string;
17+
title: string;
18+
description: React.ReactNode;
19+
badge?: string;
20+
controls?: React.ReactNode;
21+
children: React.ReactNode;
22+
}
23+
24+
export function GameReferencePageShell({
25+
icon: Icon,
26+
iconColor,
27+
iconBg,
28+
title,
29+
description,
30+
badge,
31+
controls,
32+
children,
33+
}: GameReferencePageShellProps) {
34+
return (
35+
<div className="relative min-h-screen bg-fd-background">
36+
{/* Ambient background */}
37+
<div className="pointer-events-none absolute inset-0 -z-10 overflow-hidden">
38+
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-transparent" />
39+
<div className="absolute -top-24 left-1/4 h-80 w-80 rounded-full bg-primary/8 blur-[110px] opacity-40" />
40+
<div className="absolute top-1/2 -right-20 h-64 w-64 rounded-full bg-primary/5 blur-[90px] opacity-30" />
41+
</div>
42+
43+
{/* Header */}
44+
<div className="border-b border-fd-border bg-fd-card/70 backdrop-blur-sm">
45+
<div className="mx-auto max-w-7xl px-4 pt-4 pb-5 sm:px-6 lg:px-8">
46+
{/* Breadcrumb */}
47+
<Link
48+
href="/game-references"
49+
className="mb-4 inline-flex items-center gap-1 text-xs font-medium text-fd-muted-foreground transition-colors hover:text-fd-foreground"
50+
>
51+
<ChevronLeft className="h-3.5 w-3.5" />
52+
Game References
53+
</Link>
54+
55+
<div className="flex items-start gap-4">
56+
{/* Icon */}
57+
<div
58+
className={cn(
59+
"flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-xl border bg-gradient-to-br",
60+
iconBg,
61+
)}
62+
>
63+
<Icon className={cn("h-6 w-6", iconColor)} />
64+
</div>
65+
66+
{/* Title + description */}
67+
<div className="min-w-0 flex-1">
68+
<div className="flex flex-wrap items-center gap-2.5">
69+
<h1 className="text-2xl font-bold tracking-tight text-fd-foreground">
70+
{title}
71+
</h1>
72+
{badge && (
73+
<span className="inline-flex items-center rounded-full border border-fd-border bg-fd-muted px-2.5 py-0.5 text-xs font-medium text-fd-muted-foreground tabular-nums">
74+
{badge}
75+
</span>
76+
)}
77+
</div>
78+
<p className="mt-1 text-sm leading-relaxed text-fd-muted-foreground">
79+
{description}
80+
</p>
81+
</div>
82+
</div>
83+
84+
{/* Tabs / filters / search */}
85+
{controls && <div className="mt-4 space-y-3">{controls}</div>}
86+
</div>
87+
</div>
88+
89+
{/* Main content */}
90+
<div className="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">{children}</div>
91+
</div>
92+
);
93+
}
94+
95+
// ---------------------------------------------------------------------------
96+
// ReferenceSearch — consistent search bar used across all pages
97+
// ---------------------------------------------------------------------------
98+
99+
interface ReferenceSearchProps {
100+
value: string;
101+
placeholder: string;
102+
onChange: (v: string) => void;
103+
onSearch: () => void;
104+
onClear: () => void;
105+
}
106+
107+
export function ReferenceSearch({
108+
value,
109+
placeholder,
110+
onChange,
111+
onSearch,
112+
onClear,
113+
}: ReferenceSearchProps) {
114+
return (
115+
<div className="flex gap-2">
116+
<div className="relative max-w-sm flex-1">
117+
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-fd-muted-foreground" />
118+
<Input
119+
className="pl-9 pr-9"
120+
placeholder={placeholder}
121+
value={value}
122+
onChange={(e) => onChange(e.target.value)}
123+
onKeyDown={(e) => e.key === "Enter" && onSearch()}
124+
/>
125+
{value && (
126+
<button
127+
className="absolute right-3 top-1/2 -translate-y-1/2 text-fd-muted-foreground transition-colors hover:text-fd-foreground"
128+
onClick={onClear}
129+
>
130+
<X className="h-4 w-4" />
131+
</button>
132+
)}
133+
</div>
134+
<Button onClick={onSearch}>Search</Button>
135+
</div>
136+
);
137+
}
138+
139+
// ---------------------------------------------------------------------------
140+
// ReferencePagination — consistent pagination used across all pages
141+
// ---------------------------------------------------------------------------
142+
143+
interface ReferencePaginationProps {
144+
offset: number;
145+
limit: number;
146+
total: number;
147+
hasMore: boolean;
148+
onPrev: () => void;
149+
onNext: () => void;
150+
}
151+
152+
export function ReferencePagination({
153+
offset,
154+
limit,
155+
total,
156+
hasMore,
157+
onPrev,
158+
onNext,
159+
}: ReferencePaginationProps) {
160+
if (!hasMore && offset === 0) return null;
161+
return (
162+
<div className="mt-6 flex items-center justify-center gap-3">
163+
<Button variant="outline" size="sm" disabled={offset === 0} onClick={onPrev}>
164+
Previous
165+
</Button>
166+
<span className="text-sm tabular-nums text-fd-muted-foreground">
167+
{offset + 1}{Math.min(offset + limit, total)} of {total}
168+
</span>
169+
<Button variant="outline" size="sm" disabled={!hasMore} onClick={onNext}>
170+
Next
171+
</Button>
172+
</div>
173+
);
174+
}
175+
176+
// ---------------------------------------------------------------------------
177+
// ReferenceFilterChips — pill-style filter buttons
178+
// ---------------------------------------------------------------------------
179+
180+
interface ReferenceFilterChipsProps {
181+
allLabel?: string;
182+
options: string[];
183+
value: string;
184+
onChange: (v: string) => void;
185+
}
186+
187+
export function ReferenceFilterChips({
188+
allLabel = "All",
189+
options,
190+
value,
191+
onChange,
192+
}: ReferenceFilterChipsProps) {
193+
if (options.length === 0) return null;
194+
return (
195+
<div className="flex flex-wrap gap-1.5">
196+
<Button
197+
variant={value === "" ? "default" : "outline"}
198+
size="sm"
199+
onClick={() => onChange("")}
200+
>
201+
{allLabel}
202+
</Button>
203+
{[...options].sort().map((opt) => (
204+
<Button
205+
key={opt}
206+
variant={value === opt ? "default" : "outline"}
207+
size="sm"
208+
onClick={() => onChange(opt)}
209+
>
210+
{opt}
211+
</Button>
212+
))}
213+
</div>
214+
);
215+
}
216+
217+
// ---------------------------------------------------------------------------
218+
// ReferenceTabs — horizontal tab switcher
219+
// ---------------------------------------------------------------------------
220+
221+
interface Tab {
222+
id: string;
223+
label: string;
224+
count?: number;
225+
}
226+
227+
interface ReferenceTabsProps {
228+
tabs: Tab[];
229+
active: string;
230+
onChange: (id: string) => void;
231+
}
232+
233+
export function ReferenceTabs({ tabs, active, onChange }: ReferenceTabsProps) {
234+
return (
235+
<div className="flex gap-1 rounded-lg border border-fd-border bg-fd-muted/50 p-1 w-fit">
236+
{tabs.map((tab) => (
237+
<button
238+
key={tab.id}
239+
onClick={() => onChange(tab.id)}
240+
className={cn(
241+
"rounded-md px-3 py-1.5 text-sm font-medium transition-all",
242+
active === tab.id
243+
? "bg-fd-card text-fd-foreground shadow-sm"
244+
: "text-fd-muted-foreground hover:text-fd-foreground",
245+
)}
246+
>
247+
{tab.label}
248+
{tab.count !== undefined && (
249+
<span className="ml-1.5 rounded-full bg-fd-muted px-1.5 py-0.5 text-xs tabular-nums">
250+
{tab.count}
251+
</span>
252+
)}
253+
</button>
254+
))}
255+
</div>
256+
);
257+
}

0 commit comments

Comments
 (0)