129 lines
2.8 KiB
TypeScript
129 lines
2.8 KiB
TypeScript
// Grouping for nav + layout
|
|
export type CategoryGroup = "lower" | "upper" | "accessories";
|
|
|
|
/**
|
|
* CategoryId is the canonical identifier used throughout the UI + localStorage.
|
|
*
|
|
* ✅ Canonical IDs are kebab-case to match route segments and `gunbuilderParts.ts`.
|
|
*
|
|
* 🧯 Legacy camelCase IDs are temporarily supported to avoid breaking older
|
|
* localStorage/build-state and older code paths. Prefer kebab-case going forward.
|
|
*/
|
|
export type CategoryId =
|
|
// ===== LOWER =====
|
|
| "lower-receiver"
|
|
| "complete-lower"
|
|
| "lower-parts"
|
|
| "trigger"
|
|
| "grip"
|
|
| "safety"
|
|
| "buffer"
|
|
| "stock"
|
|
|
|
// ===== UPPER =====
|
|
| "upper-receiver"
|
|
| "complete-upper"
|
|
| "barrel"
|
|
| "handguard"
|
|
| "gas-block"
|
|
| "gas-tube"
|
|
| "muzzle-device"
|
|
| "bcg"
|
|
| "charging-handle"
|
|
| "suppressor"
|
|
| "sights"
|
|
| "optic"
|
|
|
|
// ===== ACCESSORIES =====
|
|
| "magazine"
|
|
| "weapon-light"
|
|
| "foregrip"
|
|
| "bipod"
|
|
| "sling"
|
|
| "rail-accessory"
|
|
| "tools"
|
|
|
|
// ===== LEGACY (temporary) =====
|
|
| "completeUpper"
|
|
| "gasBlock"
|
|
| "gasTube"
|
|
| "muzzleDevice"
|
|
| "chargingHandle"
|
|
| "completeLower"
|
|
| "lowerParts"
|
|
| "weaponLight"
|
|
| "railAccessory";
|
|
|
|
export interface Category {
|
|
id: CategoryId;
|
|
name: string;
|
|
description?: string;
|
|
/** used for nav & grouping */
|
|
group?: CategoryGroup;
|
|
/** optional slug override (mostly for legacy IDs) */
|
|
slug?: string;
|
|
}
|
|
|
|
export interface Part {
|
|
id: string;
|
|
name: string;
|
|
brand: string;
|
|
categoryId: CategoryId;
|
|
price: number;
|
|
affiliateUrl?: string;
|
|
url?: string;
|
|
imageUrl?: string;
|
|
notes?: string;
|
|
}
|
|
|
|
/**
|
|
* Route / UI slug for each CategoryId.
|
|
*
|
|
* - Canonical kebab-case ids map 1:1.
|
|
* - Legacy camelCase ids map to the same slug.
|
|
*/
|
|
export const CATEGORY_SLUGS = {
|
|
// LOWER
|
|
"lower-receiver": "lower-receiver",
|
|
"complete-lower": "complete-lower",
|
|
"lower-parts": "lower-parts",
|
|
trigger: "trigger",
|
|
grip: "grip",
|
|
safety: "safety",
|
|
buffer: "buffer",
|
|
stock: "stock",
|
|
|
|
// UPPER
|
|
"upper-receiver": "upper-receiver",
|
|
"complete-upper": "complete-upper",
|
|
barrel: "barrel",
|
|
handguard: "handguard",
|
|
"gas-block": "gas-block",
|
|
"gas-tube": "gas-tube",
|
|
"muzzle-device": "muzzle-device",
|
|
sights: "sights",
|
|
bcg: "bcg",
|
|
"charging-handle": "charging-handle",
|
|
suppressor: "suppressor",
|
|
optic: "optic",
|
|
|
|
// ACCESSORIES
|
|
magazine: "magazine",
|
|
"weapon-light": "weapon-light",
|
|
foregrip: "foregrip",
|
|
bipod: "bipod",
|
|
sling: "sling",
|
|
"rail-accessory": "rail-accessory",
|
|
tools: "tools",
|
|
|
|
// LEGACY (temporary)
|
|
completeUpper: "complete-upper",
|
|
gasBlock: "gas-block",
|
|
gasTube: "gas-tube",
|
|
muzzleDevice: "muzzle-device",
|
|
chargingHandle: "charging-handle",
|
|
completeLower: "complete-lower",
|
|
lowerParts: "lower-parts",
|
|
weaponLight: "weapon-light",
|
|
railAccessory: "rail-accessory",
|
|
} as const satisfies Record<CategoryId, string>; |