full app is using live data now,
This commit is contained in:
+12
-17
@@ -8,11 +8,9 @@ import type { CategoryId, Part } from "@/types/gunbuilder";
|
||||
import { CategoryColumn } from "@/components/CategoryColumn";
|
||||
|
||||
type GunbuilderProductFromApi = {
|
||||
id: number;
|
||||
uuid: string;
|
||||
slug: string;
|
||||
id: number; // backend numeric id
|
||||
name: string;
|
||||
brandName: string;
|
||||
brand: string;
|
||||
platform: string;
|
||||
partRole: string;
|
||||
price: number | null;
|
||||
@@ -24,7 +22,6 @@ const API_BASE_URL =
|
||||
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080";
|
||||
|
||||
// Map backend partRole to your existing CategoryId values.
|
||||
// Make sure these string values match the `id` fields in CATEGORIES.
|
||||
const PART_ROLE_TO_CATEGORY: Record<string, CategoryId> = {
|
||||
"upper-receiver": "upper" as CategoryId,
|
||||
barrel: "barrel" as CategoryId,
|
||||
@@ -46,7 +43,6 @@ export default function GunbuilderPage() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [build, setBuild] = useState<BuildState>(() => {
|
||||
// Initialize from localStorage if available
|
||||
if (typeof window !== "undefined") {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
@@ -80,7 +76,7 @@ export default function GunbuilderPage() {
|
||||
const data: GunbuilderProductFromApi[] = await res.json();
|
||||
|
||||
const normalized: Part[] = data
|
||||
.map((p) => {
|
||||
.map((p): Part | null => {
|
||||
const categoryId = PART_ROLE_TO_CATEGORY[p.partRole];
|
||||
if (!categoryId) {
|
||||
// Skip any parts we don't know how to map yet
|
||||
@@ -88,17 +84,17 @@ export default function GunbuilderPage() {
|
||||
}
|
||||
|
||||
return {
|
||||
// Use the backend UUID as the stable id for the UI
|
||||
id: p.uuid,
|
||||
id: String(p.id), // <<< ALWAYS string
|
||||
categoryId,
|
||||
name: p.name,
|
||||
brand: p.brandName,
|
||||
brand: p.brand,
|
||||
price: p.price ?? 0,
|
||||
imageUrl: p.mainImageUrl ?? undefined,
|
||||
url: p.buyUrl ?? undefined,
|
||||
} as Part;
|
||||
notes: undefined,
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as Part[];
|
||||
.filter((p): p is Part => p !== null);
|
||||
|
||||
setParts(normalized);
|
||||
} catch (err: any) {
|
||||
@@ -114,7 +110,7 @@ export default function GunbuilderPage() {
|
||||
return () => controller.abort();
|
||||
}, []);
|
||||
|
||||
// Handle URL query parameter for part selection
|
||||
// Handle URL query parameter for part selection (?select=upper:165)
|
||||
useEffect(() => {
|
||||
const selectParam = searchParams.get("select");
|
||||
if (selectParam) {
|
||||
@@ -123,13 +119,12 @@ export default function GunbuilderPage() {
|
||||
setBuild((prev) => {
|
||||
const updated = {
|
||||
...prev,
|
||||
[categoryId]: partId,
|
||||
[categoryId as CategoryId]: partId,
|
||||
};
|
||||
// Persist to localStorage
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
|
||||
return updated;
|
||||
});
|
||||
// Remove query param from URL
|
||||
|
||||
router.replace("/gunbuilder", { scroll: false });
|
||||
}
|
||||
}
|
||||
@@ -319,4 +314,4 @@ export default function GunbuilderPage() {
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user