diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/ProfileLoading.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/ProfileLoading.tsx new file mode 100644 index 0000000000..13b67383eb --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/ProfileLoading.tsx @@ -0,0 +1,48 @@ +import { Skeleton } from "@/components/__legacy__/ui/skeleton"; +import { Separator } from "@radix-ui/react-separator"; + +export function ProfileLoading() { + return ( +
+
+ +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ {[1, 2, 3, 4, 5].map((i) => ( +
+ + +
+ ))} +
+
+ +
+ +
+
+
+
+
+ ); +} diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/page.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/page.tsx index 86190e62cc..260fbc0b52 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/page.tsx @@ -1,23 +1,58 @@ -import React from "react"; -import { Metadata } from "next/types"; -import { redirect } from "next/navigation"; -import BackendAPI from "@/lib/autogpt-server-api"; +"use client"; + +import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store"; import { ProfileInfoForm } from "@/components/__legacy__/ProfileInfoForm"; +import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard"; +import { ProfileDetails } from "@/lib/autogpt-server-api/types"; +import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; +import { ProfileLoading } from "./ProfileLoading"; -// Force dynamic rendering to avoid static generation issues with cookies -export const dynamic = "force-dynamic"; +export default function UserProfilePage() { + const { user } = useSupabase(); -export const metadata: Metadata = { title: "Profile - AutoGPT Platform" }; - -export default async function UserProfilePage(): Promise { - const api = new BackendAPI(); - const profile = await api.getStoreProfile().catch((error) => { - console.error("Error fetching profile:", error); - return null; + const { + data: profile, + isLoading, + isError, + error, + refetch, + } = useGetV2GetUserProfile({ + query: { + enabled: !!user, + select: (res) => { + if (res.status === 200) { + return { + ...res.data, + avatar_url: res.data.avatar_url ?? "", + }; + } + return null; + }, + }, }); - if (!profile) { - redirect("/login"); + if (isError) { + return ( +
+ { + void refetch(); + }} + /> +
+ ); + } + + if (isLoading || !user || !profile) { + return ; } return ( diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm.tsx index 127b7ac94f..5a42db20c0 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm.tsx @@ -1,7 +1,7 @@ "use client"; -import { Separator } from "@/components/__legacy__/ui/separator"; import { NotificationPreference } from "@/app/api/__generated__/models/notificationPreference"; +import { Separator } from "@/components/__legacy__/ui/separator"; import { User } from "@supabase/supabase-js"; import { EmailForm } from "./components/EmailForm/EmailForm"; import { NotificationForm } from "./components/NotificationForm/NotificationForm"; @@ -18,6 +18,8 @@ export function SettingsForm({ user, timezone, }: SettingsFormProps) { + if (!user || !preferences) return null; + return (
diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/TimezoneForm/TimezoneForm.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/TimezoneForm/TimezoneForm.tsx index 0456e901d0..f09dc11afc 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/TimezoneForm/TimezoneForm.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/TimezoneForm/TimezoneForm.tsx @@ -1,22 +1,11 @@ "use client"; -import * as React from "react"; -import { useTimezoneForm } from "./useTimezoneForm"; -import { User } from "@supabase/supabase-js"; import { Card, CardContent, CardHeader, CardTitle, } from "@/components/__legacy__/ui/card"; -import { Button } from "@/components/atoms/Button/Button"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/__legacy__/ui/select"; import { Form, FormControl, @@ -25,48 +14,26 @@ import { FormLabel, FormMessage, } from "@/components/__legacy__/ui/form"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/__legacy__/ui/select"; +import { Button } from "@/components/atoms/Button/Button"; +import { User } from "@supabase/supabase-js"; +import * as React from "react"; +import { TIMEZONES } from "./helpers"; +import { useTimezoneForm } from "./useTimezoneForm"; -type TimezoneFormProps = { +type Props = { user: User; currentTimezone?: string; }; -// Common timezones list - can be expanded later -const TIMEZONES = [ - { value: "UTC", label: "UTC (Coordinated Universal Time)" }, - { value: "America/New_York", label: "Eastern Time (US & Canada)" }, - { value: "America/Chicago", label: "Central Time (US & Canada)" }, - { value: "America/Denver", label: "Mountain Time (US & Canada)" }, - { value: "America/Los_Angeles", label: "Pacific Time (US & Canada)" }, - { value: "America/Phoenix", label: "Arizona (US)" }, - { value: "America/Anchorage", label: "Alaska (US)" }, - { value: "Pacific/Honolulu", label: "Hawaii (US)" }, - { value: "Europe/London", label: "London (UK)" }, - { value: "Europe/Paris", label: "Paris (France)" }, - { value: "Europe/Berlin", label: "Berlin (Germany)" }, - { value: "Europe/Moscow", label: "Moscow (Russia)" }, - { value: "Asia/Dubai", label: "Dubai (UAE)" }, - { value: "Asia/Kolkata", label: "India Standard Time" }, - { value: "Asia/Shanghai", label: "China Standard Time" }, - { value: "Asia/Tokyo", label: "Tokyo (Japan)" }, - { value: "Asia/Seoul", label: "Seoul (South Korea)" }, - { value: "Asia/Singapore", label: "Singapore" }, - { value: "Australia/Sydney", label: "Sydney (Australia)" }, - { value: "Australia/Melbourne", label: "Melbourne (Australia)" }, - { value: "Pacific/Auckland", label: "Auckland (New Zealand)" }, - { value: "America/Toronto", label: "Toronto (Canada)" }, - { value: "America/Vancouver", label: "Vancouver (Canada)" }, - { value: "America/Mexico_City", label: "Mexico City (Mexico)" }, - { value: "America/Sao_Paulo", label: "São Paulo (Brazil)" }, - { value: "America/Buenos_Aires", label: "Buenos Aires (Argentina)" }, - { value: "Africa/Cairo", label: "Cairo (Egypt)" }, - { value: "Africa/Johannesburg", label: "Johannesburg (South Africa)" }, -]; - -export function TimezoneForm({ - user, - currentTimezone = "not-set", -}: TimezoneFormProps) { +export function TimezoneForm({ user, currentTimezone = "not-set" }: Props) { + console.log("currentTimezone", currentTimezone); // If timezone is not set, try to detect it from the browser const effectiveTimezone = React.useMemo(() => { if (currentTimezone === "not-set") { @@ -120,7 +87,7 @@ export function TimezoneForm({ )} /> - diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/TimezoneForm/helpers.ts b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/TimezoneForm/helpers.ts new file mode 100644 index 0000000000..1b62a8ce45 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/TimezoneForm/helpers.ts @@ -0,0 +1,123 @@ +export const TIMEZONES = [ + { value: "UTC", label: "UTC (Coordinated Universal Time)" }, + { value: "America/Adak", label: "Adak (US - Aleutian Islands)" }, + { value: "America/Anchorage", label: "Anchorage (US - Alaska)" }, + { value: "America/Los_Angeles", label: "Los Angeles (US - Pacific)" }, + { value: "America/Tijuana", label: "Tijuana (Mexico - Pacific)" }, + { value: "America/Phoenix", label: "Phoenix (US - Arizona)" }, + { value: "America/Denver", label: "Denver (US - Mountain)" }, + { value: "America/Chicago", label: "Chicago (US - Central)" }, + { value: "America/New_York", label: "New York (US - Eastern)" }, + { value: "America/Toronto", label: "Toronto (Canada - Eastern)" }, + { value: "America/Halifax", label: "Halifax (Canada - Atlantic)" }, + { value: "America/St_Johns", label: "St. John's (Canada - Newfoundland)" }, + { value: "America/Caracas", label: "Caracas (Venezuela)" }, + { value: "America/Bogota", label: "Bogotá (Colombia)" }, + { value: "America/Lima", label: "Lima (Peru)" }, + { value: "America/Santiago", label: "Santiago (Chile)" }, + { value: "America/La_Paz", label: "La Paz (Bolivia)" }, + { value: "America/Asuncion", label: "Asunción (Paraguay)" }, + { value: "America/Montevideo", label: "Montevideo (Uruguay)" }, + { value: "America/Buenos_Aires", label: "Buenos Aires (Argentina)" }, + { value: "America/Sao_Paulo", label: "São Paulo (Brazil)" }, + { value: "America/Manaus", label: "Manaus (Brazil - Amazon)" }, + { value: "America/Fortaleza", label: "Fortaleza (Brazil - Northeast)" }, + { value: "America/Mexico_City", label: "Mexico City (Mexico)" }, + { value: "America/Guatemala", label: "Guatemala" }, + { value: "America/Costa_Rica", label: "Costa Rica" }, + { value: "America/Panama", label: "Panama" }, + { value: "America/Havana", label: "Havana (Cuba)" }, + { value: "America/Jamaica", label: "Jamaica" }, + { value: "America/Port-au-Prince", label: "Port-au-Prince (Haiti)" }, + { + value: "America/Santo_Domingo", + label: "Santo Domingo (Dominican Republic)", + }, + { value: "America/Puerto_Rico", label: "Puerto Rico" }, + { value: "Atlantic/Azores", label: "Azores (Portugal)" }, + { value: "Atlantic/Cape_Verde", label: "Cape Verde" }, + { value: "Europe/London", label: "London (UK)" }, + { value: "Europe/Dublin", label: "Dublin (Ireland)" }, + { value: "Europe/Lisbon", label: "Lisbon (Portugal)" }, + { value: "Europe/Madrid", label: "Madrid (Spain)" }, + { value: "Europe/Paris", label: "Paris (France)" }, + { value: "Europe/Brussels", label: "Brussels (Belgium)" }, + { value: "Europe/Amsterdam", label: "Amsterdam (Netherlands)" }, + { value: "Europe/Berlin", label: "Berlin (Germany)" }, + { value: "Europe/Rome", label: "Rome (Italy)" }, + { value: "Europe/Vienna", label: "Vienna (Austria)" }, + { value: "Europe/Zurich", label: "Zurich (Switzerland)" }, + { value: "Europe/Prague", label: "Prague (Czech Republic)" }, + { value: "Europe/Warsaw", label: "Warsaw (Poland)" }, + { value: "Europe/Stockholm", label: "Stockholm (Sweden)" }, + { value: "Europe/Oslo", label: "Oslo (Norway)" }, + { value: "Europe/Copenhagen", label: "Copenhagen (Denmark)" }, + { value: "Europe/Helsinki", label: "Helsinki (Finland)" }, + { value: "Europe/Athens", label: "Athens (Greece)" }, + { value: "Europe/Bucharest", label: "Bucharest (Romania)" }, + { value: "Europe/Sofia", label: "Sofia (Bulgaria)" }, + { value: "Europe/Budapest", label: "Budapest (Hungary)" }, + { value: "Europe/Belgrade", label: "Belgrade (Serbia)" }, + { value: "Europe/Zagreb", label: "Zagreb (Croatia)" }, + { value: "Europe/Moscow", label: "Moscow (Russia)" }, + { value: "Europe/Kiev", label: "Kyiv (Ukraine)" }, + { value: "Europe/Istanbul", label: "Istanbul (Turkey)" }, + { value: "Asia/Dubai", label: "Dubai (UAE)" }, + { value: "Asia/Muscat", label: "Muscat (Oman)" }, + { value: "Asia/Kuwait", label: "Kuwait" }, + { value: "Asia/Riyadh", label: "Riyadh (Saudi Arabia)" }, + { value: "Asia/Baghdad", label: "Baghdad (Iraq)" }, + { value: "Asia/Tehran", label: "Tehran (Iran)" }, + { value: "Asia/Kabul", label: "Kabul (Afghanistan)" }, + { value: "Asia/Karachi", label: "Karachi (Pakistan)" }, + { value: "Asia/Tashkent", label: "Tashkent (Uzbekistan)" }, + { value: "Asia/Dhaka", label: "Dhaka (Bangladesh)" }, + { value: "Asia/Kolkata", label: "Kolkata (India)" }, + { value: "Asia/Kathmandu", label: "Kathmandu (Nepal)" }, + { value: "Asia/Colombo", label: "Colombo (Sri Lanka)" }, + { value: "Asia/Yangon", label: "Yangon (Myanmar)" }, + { value: "Asia/Bangkok", label: "Bangkok (Thailand)" }, + { value: "Asia/Ho_Chi_Minh", label: "Ho Chi Minh City (Vietnam)" }, + { value: "Asia/Jakarta", label: "Jakarta (Indonesia - Western)" }, + { value: "Asia/Makassar", label: "Makassar (Indonesia - Central)" }, + { value: "Asia/Jayapura", label: "Jayapura (Indonesia - Eastern)" }, + { value: "Asia/Manila", label: "Manila (Philippines)" }, + { value: "Asia/Singapore", label: "Singapore" }, + { value: "Asia/Kuala_Lumpur", label: "Kuala Lumpur (Malaysia)" }, + { value: "Asia/Hong_Kong", label: "Hong Kong" }, + { value: "Asia/Shanghai", label: "Shanghai (China)" }, + { value: "Asia/Taipei", label: "Taipei (Taiwan)" }, + { value: "Asia/Seoul", label: "Seoul (South Korea)" }, + { value: "Asia/Tokyo", label: "Tokyo (Japan)" }, + { value: "Asia/Vladivostok", label: "Vladivostok (Russia)" }, + { value: "Asia/Yakutsk", label: "Yakutsk (Russia)" }, + { value: "Asia/Irkutsk", label: "Irkutsk (Russia)" }, + { value: "Asia/Yekaterinburg", label: "Yekaterinburg (Russia)" }, + { value: "Australia/Perth", label: "Perth (Australia - Western)" }, + { value: "Australia/Darwin", label: "Darwin (Australia - Northern)" }, + { value: "Australia/Adelaide", label: "Adelaide (Australia - Central)" }, + { value: "Australia/Brisbane", label: "Brisbane (Australia - Eastern)" }, + { value: "Australia/Sydney", label: "Sydney (Australia - Eastern)" }, + { value: "Australia/Melbourne", label: "Melbourne (Australia - Eastern)" }, + { value: "Australia/Hobart", label: "Hobart (Australia - Eastern)" }, + { value: "Pacific/Auckland", label: "Auckland (New Zealand)" }, + { value: "Pacific/Fiji", label: "Fiji" }, + { value: "Pacific/Guam", label: "Guam" }, + { value: "Pacific/Honolulu", label: "Honolulu (US - Hawaii)" }, + { value: "Pacific/Samoa", label: "Samoa" }, + { value: "Pacific/Tahiti", label: "Tahiti (French Polynesia)" }, + { value: "Africa/Cairo", label: "Cairo (Egypt)" }, + { value: "Africa/Johannesburg", label: "Johannesburg (South Africa)" }, + { value: "Africa/Lagos", label: "Lagos (Nigeria)" }, + { value: "Africa/Nairobi", label: "Nairobi (Kenya)" }, + { value: "Africa/Casablanca", label: "Casablanca (Morocco)" }, + { value: "Africa/Algiers", label: "Algiers (Algeria)" }, + { value: "Africa/Tunis", label: "Tunis (Tunisia)" }, + { value: "Africa/Addis_Ababa", label: "Addis Ababa (Ethiopia)" }, + { value: "Africa/Dar_es_Salaam", label: "Dar es Salaam (Tanzania)" }, + { value: "Africa/Kampala", label: "Kampala (Uganda)" }, + { value: "Africa/Khartoum", label: "Khartoum (Sudan)" }, + { value: "Africa/Accra", label: "Accra (Ghana)" }, + { value: "Africa/Abidjan", label: "Abidjan (Ivory Coast)" }, + { value: "Africa/Dakar", label: "Dakar (Senegal)" }, +]; diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/page.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/page.tsx index 679a145290..f0eb8a6b8c 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/page.tsx @@ -1,4 +1,5 @@ "use client"; + import { SettingsForm } from "@/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm"; import { useTimezoneDetection } from "@/app/(platform)/profile/(user)/settings/useTimezoneDetection"; import { @@ -6,49 +7,67 @@ import { useGetV1GetUserTimezone, } from "@/app/api/__generated__/endpoints/auth/auth"; import { Text } from "@/components/atoms/Text/Text"; +import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard"; import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; -import { redirect } from "next/navigation"; import { useEffect } from "react"; import SettingsLoading from "./loading"; export default function SettingsPage() { + const { user } = useSupabase(); + const { data: preferences, isError: preferencesError, isLoading: preferencesLoading, + error: preferencesErrorData, + refetch: refetchPreferences, } = useGetV1GetNotificationPreferences({ - query: { select: (res) => (res.status === 200 ? res.data : null) }, + query: { + enabled: !!user, + select: (res) => (res.status === 200 ? res.data : null), + }, }); const { data: timezone, isLoading: timezoneLoading } = useGetV1GetUserTimezone({ query: { + enabled: !!user, select: (res) => { return res.status === 200 ? String(res.data.timezone) : "not-set"; }, }, }); - useTimezoneDetection(timezone); - - const { user, isUserLoading } = useSupabase(); + useTimezoneDetection(!!user ? timezone : undefined); useEffect(() => { document.title = "Settings – AutoGPT Platform"; }, []); - if (preferencesLoading || isUserLoading || timezoneLoading) { + if (preferencesError) { + return ( +
+ { + void refetchPreferences(); + }} + /> +
+ ); + } + + if (preferencesLoading || timezoneLoading || !user || !preferences) { return ; } - if (!user) { - redirect("/login"); - } - - if (preferencesError || !preferences || !preferences.preferences) { - return "Error..."; // TODO: Will use a Error reusable components from Block Menu redesign - } - return (
diff --git a/autogpt_platform/frontend/src/app/layout.tsx b/autogpt_platform/frontend/src/app/layout.tsx index dd73303b9e..ff2590dc61 100644 --- a/autogpt_platform/frontend/src/app/layout.tsx +++ b/autogpt_platform/frontend/src/app/layout.tsx @@ -6,6 +6,7 @@ import "./globals.css"; import { Providers } from "@/app/providers"; import { CookieConsentBanner } from "@/components/molecules/CookieConsentBanner/CookieConsentBanner"; +import { ErrorBoundary } from "@/components/molecules/ErrorBoundary/ErrorBoundary"; import TallyPopupSimple from "@/components/molecules/TallyPoup/TallyPopup"; import { Toaster } from "@/components/molecules/Toast/toaster"; import { SetupAnalytics } from "@/services/analytics"; @@ -54,29 +55,31 @@ export default async function RootLayout({ /> - -
- {children} - - + + +
+ {children} + + - {/* React Query DevTools is only available in development */} - {process.env.NEXT_PUBLIC_REACT_QUERY_DEVTOOL && ( - - )} -
- - -
+ {/* React Query DevTools is only available in development */} + {process.env.NEXT_PUBLIC_REACT_QUERY_DEVTOOL && ( + + )} +
+ + +
+ ); diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AccountMenu/components/AccountLogoutOption.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/AccountMenu/components/AccountLogoutOption.tsx index ec6f65d0a6..74bd24bfc1 100644 --- a/autogpt_platform/frontend/src/components/layout/Navbar/components/AccountMenu/components/AccountLogoutOption.tsx +++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AccountMenu/components/AccountLogoutOption.tsx @@ -5,17 +5,20 @@ import { useToast } from "@/components/molecules/Toast/use-toast"; import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; import { cn } from "@/lib/utils"; import * as Sentry from "@sentry/nextjs"; +import { useRouter } from "next/navigation"; import { useState } from "react"; export function AccountLogoutOption() { const [isLoggingOut, setIsLoggingOut] = useState(false); const supabase = useSupabase(); + const router = useRouter(); const { toast } = useToast(); async function handleLogout() { setIsLoggingOut(true); try { await supabase.logOut(); + router.push("/login"); } catch (e) { Sentry.captureException(e); toast({ diff --git a/autogpt_platform/frontend/src/components/molecules/ErrorBoundary/ErrorBoundary.tsx b/autogpt_platform/frontend/src/components/molecules/ErrorBoundary/ErrorBoundary.tsx new file mode 100644 index 0000000000..879209f692 --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/ErrorBoundary/ErrorBoundary.tsx @@ -0,0 +1,78 @@ +"use client"; + +import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard"; +import * as Sentry from "@sentry/nextjs"; +import { Component, type ReactNode } from "react"; + +interface ErrorBoundaryProps { + children: ReactNode; + fallback?: ReactNode; + context?: string; + onReset?: () => void; +} + +interface ErrorBoundaryState { + hasError: boolean; + error: Error | null; +} + +export class ErrorBoundary extends Component< + ErrorBoundaryProps, + ErrorBoundaryState +> { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { hasError: false, error: null }; + } + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + Sentry.captureException(error, { + contexts: { + react: { + componentStack: errorInfo.componentStack, + }, + }, + tags: { + errorBoundary: "true", + context: this.props.context || "application", + }, + }); + } + + handleReset = () => { + this.setState({ hasError: false, error: null }); + if (this.props.onReset) { + this.props.onReset(); + } + }; + + render() { + if (this.state.hasError && this.state.error) { + if (this.props.fallback) { + return this.props.fallback; + } + + return ( +
+
+ +
+
+ ); + } + + return this.props.children; + } +} diff --git a/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabase.ts b/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabase.ts index 81ad1961ea..41fdee25a2 100644 --- a/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabase.ts +++ b/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabase.ts @@ -44,7 +44,6 @@ export function useSupabase() { return logOut({ options, api, - router, }); } diff --git a/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabaseStore.ts b/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabaseStore.ts index 90536285a5..a8eb82c93b 100644 --- a/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabaseStore.ts +++ b/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabaseStore.ts @@ -72,10 +72,29 @@ export const useSupabaseStore = create((set, get) => { if (!initializationPromise) { initializationPromise = (async () => { - if (!get().hasLoadedUser) { + // Always fetch user if we haven't loaded it yet, or if user is null but hasLoadedUser is true + // This handles the case where hasLoadedUser might be stale after logout/login + if (!get().hasLoadedUser || !get().user) { set({ isUserLoading: true }); const result = await fetchUser(); set(result); + + // If fetchUser didn't return a user, validate the session to ensure we have the latest state + // This handles race conditions after login where cookies might not be immediately available + if (!result.user) { + const validationResult = await validateSessionHelper({ + pathname: params.pathname, + currentUser: null, + }); + + if (validationResult.user && validationResult.isValid) { + set({ + user: validationResult.user, + hasLoadedUser: true, + isUserLoading: false, + }); + } + } } else { set({ isUserLoading: false }); } @@ -104,7 +123,6 @@ export const useSupabaseStore = create((set, get) => { } async function logOut(params?: LogOutParams): Promise { - const router = params?.router ?? get().routerRef; const api = params?.api ?? get().apiRef; const options = params?.options ?? {}; @@ -128,11 +146,7 @@ export const useSupabaseStore = create((set, get) => { isUserLoading: false, }); - const result = await serverLogout(options); - - if (result.success && router) { - router.push("/login"); - } + await serverLogout(options); } async function validateSessionInternal(