diff --git a/src/client/routes/__root.tsx b/src/client/routes/__root.tsx index f8c79cd..cc4554b 100644 --- a/src/client/routes/__root.tsx +++ b/src/client/routes/__root.tsx @@ -12,6 +12,7 @@ import { Toaster } from "sonner"; import "../app.css"; import { AddToCollectionModal } from "../components/AddToCollectionModal"; import { AddToThreadModal } from "../components/AddToThreadModal"; +import { AuthPromptModal } from "../components/AuthPromptModal"; import { CatalogSearchOverlay } from "../components/CatalogSearchOverlay"; import { ConfirmDialog } from "../components/ConfirmDialog"; import { ExternalLinkDialog } from "../components/ExternalLinkDialog"; @@ -94,7 +95,7 @@ function RootLayout() { // Onboarding — only check when authenticated (endpoint requires auth) const { data: onboardingComplete, isLoading: onboardingLoading } = - useOnboardingComplete(); + useOnboardingComplete(isAuthenticated); const [wizardDismissed, setWizardDismissed] = useState(false); // Don't show onboarding wizard until user has created an account @@ -117,40 +118,21 @@ function RootLayout() { const totalsBarProps = isDashboard ? {} : { linkTo: "/" }; - // Show loading while checking auth - if (authLoading) { - return ( -
-
-
- ); - } - - // Redirect unauthenticated users to login (server-side OIDC route) // Allow public routes through without auth const isPublicRoute = - location.pathname.startsWith("/users/") || location.pathname === "/login"; + location.pathname === "/" || + location.pathname.startsWith("/users/") || + location.pathname.startsWith("/global-items") || + location.pathname.startsWith("/setups/") || + location.pathname === "/login"; // FAB visibility: show on all authenticated, non-public routes const isSetupsPage = !!matchRoute({ to: "/setups", fuzzy: true }); const showFab = isAuthenticated && !isPublicRoute; - if (!isAuthenticated && !isPublicRoute) { - window.location.href = "/login"; - return ( -
-

Redirecting to login...

-
- ); - } - - // Show loading while checking onboarding status - if (onboardingLoading) { - return ( -
-
-
- ); + if (!isAuthenticated && !isPublicRoute && !authLoading) { + navigate({ to: "/login" }); + return null; } return ( @@ -198,6 +180,9 @@ function RootLayout() { + {/* Auth Prompt Modal */} + + {/* Onboarding Wizard */} {showWizard && ( setWizardDismissed(true)} /> diff --git a/src/client/routes/global-items/$globalItemId.tsx b/src/client/routes/global-items/$globalItemId.tsx index 18d6724..80b2af7 100644 --- a/src/client/routes/global-items/$globalItemId.tsx +++ b/src/client/routes/global-items/$globalItemId.tsx @@ -1,4 +1,5 @@ import { createFileRoute, Link } from "@tanstack/react-router"; +import { useAuth } from "../../hooks/useAuth"; import { useFormatters } from "../../hooks/useFormatters"; import { useGlobalItem } from "../../hooks/useGlobalItems"; import { useUIStore } from "../../stores/uiStore"; @@ -11,8 +12,11 @@ function GlobalItemDetail() { const { globalItemId } = Route.useParams(); const { data: item, isLoading, error } = useGlobalItem(Number(globalItemId)); const { weight, price } = useFormatters(); + const { data: auth } = useAuth(); + const isAuthenticated = !!auth?.user; const openAddToCollection = useUIStore((s) => s.openAddToCollection); const openAddToThread = useUIStore((s) => s.openAddToThread); + const openAuthPrompt = useUIStore((s) => s.openAuthPrompt); if (isLoading) { return ( @@ -133,18 +137,26 @@ function GlobalItemDetail() {
- {/* Actions */} -
- + + + + Add Items + - {/* Public toggle */} - + + + + + + {setup.isPublic ? "Public" : "Private"} + -
- -
+
+ +
+ )} {/* Empty state */} {itemCount === 0 && ( @@ -214,13 +227,15 @@ function SetupDetailPage() {

Add items from your collection to build this loadout.

- + {isAuthenticated && ( + + )}
)} @@ -268,15 +283,22 @@ function SetupDetailPage() { imageFilename={item.imageFilename} imageUrl={item.imageUrl} productUrl={item.productUrl} - onRemove={() => removeItem.mutate(item.id)} + onRemove={ + isAuthenticated + ? () => removeItem.mutate(item.id) + : undefined + } classification={item.classification} - onClassificationCycle={() => - updateClassification.mutate({ - itemId: item.id, - classification: nextClassification( - item.classification, - ), - }) + onClassificationCycle={ + isAuthenticated + ? () => + updateClassification.mutate({ + itemId: item.id, + classification: nextClassification( + item.classification, + ), + }) + : undefined } /> ))} @@ -288,16 +310,18 @@ function SetupDetailPage() { )} - {/* Item Picker */} - setPickerOpen(false)} - /> + {/* Item Picker — only for authenticated users */} + {isAuthenticated && ( + setPickerOpen(false)} + /> + )} - {/* Delete Confirmation Dialog */} - {confirmDelete && ( + {/* Delete Confirmation Dialog — only for authenticated users */} + {isAuthenticated && confirmDelete && (