diff --git a/app/(app)/(builder)/builder/page.tsx b/app/(app)/(builder)/builder/page.tsx index 5125302..d6d51b6 100644 --- a/app/(app)/(builder)/builder/page.tsx +++ b/app/(app)/(builder)/builder/page.tsx @@ -356,6 +356,8 @@ function GunbuilderPageContent() { // Parts data state // ----------------------------- const [parts, setParts] = useState([]); + const previousPartIdsRef = useRef>(new Set()); + const isFirstPartsHydrationRef = useRef(true); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -668,6 +670,20 @@ function GunbuilderPageContent() { setParts(hydrated); + // Track newly selected parts (fires after API returns with real names). + // Skip the first hydration — that's restoring a saved build, not a new selection. + if (isFirstPartsHydrationRef.current) { + isFirstPartsHydrationRef.current = false; + } else { + const prevIds = previousPartIdsRef.current; + for (const p of hydrated) { + if (!prevIds.has(String(p.id))) { + trackPartSelected(p.categoryId as BuilderSlotKey, p.name, String(p.id)); + } + } + } + previousPartIdsRef.current = new Set(hydrated.map((p) => String(p.id))); + // ✅ Mark key as hydrated ONLY after success lastHydrateKeyRef.current = key; } catch (err: any) { @@ -904,10 +920,8 @@ function GunbuilderPageContent() { } return next; }); - const partName = parts.find((p) => String(p.id) === String(partId))?.name ?? String(partId); - trackPartSelected(categoryId, partName, String(partId)); }, - [build, parts] + [build] ); // Handle URL query parameters (?select, ?remove) and keep platform synced + canonical useEffect(() => {