Skip to content

Commit 8daec53

Browse files
committed
hotfix(frontend): add profile loading state and error boundary
1 parent ec6f593 commit 8daec53

File tree

11 files changed

+399
-108
lines changed

11 files changed

+399
-108
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Skeleton } from "@/components/__legacy__/ui/skeleton";
2+
import { Separator } from "@radix-ui/react-separator";
3+
4+
export function ProfileLoading() {
5+
return (
6+
<div className="flex flex-col items-center justify-center px-4">
7+
<div className="w-full min-w-[800px] px-4 sm:px-8">
8+
<Skeleton className="mb-6 h-[35px] w-32 sm:mb-8" />
9+
<div className="mb-8 sm:mb-12">
10+
<div className="mb-8 flex flex-col items-center gap-4 sm:flex-row sm:items-start">
11+
<Skeleton className="h-[130px] w-[130px] rounded-full" />
12+
<Skeleton className="mt-11 h-[43px] w-32 rounded-[22px]" />
13+
</div>
14+
<div className="space-y-4 sm:space-y-6">
15+
<div className="w-full space-y-2">
16+
<Skeleton className="h-4 w-24" />
17+
<Skeleton className="h-10 w-full rounded-[55px]" />
18+
</div>
19+
<div className="w-full space-y-2">
20+
<Skeleton className="h-4 w-16" />
21+
<Skeleton className="h-10 w-full rounded-[55px]" />
22+
</div>
23+
<div className="w-full space-y-2">
24+
<Skeleton className="h-4 w-12" />
25+
<Skeleton className="h-[220px] w-full rounded-2xl" />
26+
</div>
27+
<section className="mb-8">
28+
<Skeleton className="mb-4 h-6 w-32" />
29+
<Skeleton className="mb-6 h-4 w-64" />
30+
<div className="space-y-4 sm:space-y-6">
31+
{[1, 2, 3, 4, 5].map((i) => (
32+
<div key={i} className="w-full space-y-2">
33+
<Skeleton className="h-4 w-16" />
34+
<Skeleton className="h-10 w-full rounded-[55px]" />
35+
</div>
36+
))}
37+
</div>
38+
</section>
39+
<Separator />
40+
<div className="flex h-[50px] items-center justify-end gap-3 py-8">
41+
<Skeleton className="h-[50px] w-32 rounded-[35px]" />
42+
</div>
43+
</div>
44+
</div>
45+
</div>
46+
</div>
47+
);
48+
}

autogpt_platform/frontend/src/app/(platform)/profile/(user)/page.tsx

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,58 @@
1-
import React from "react";
2-
import { Metadata } from "next/types";
3-
import { redirect } from "next/navigation";
4-
import BackendAPI from "@/lib/autogpt-server-api";
5-
import { ProfileInfoForm } from "@/components/__legacy__/ProfileInfoForm";
1+
"use client";
62

7-
// Force dynamic rendering to avoid static generation issues with cookies
8-
export const dynamic = "force-dynamic";
3+
import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store";
4+
import { ProfileInfoForm } from "@/components/__legacy__/ProfileInfoForm";
5+
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
6+
import { ProfileDetails } from "@/lib/autogpt-server-api/types";
7+
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
8+
import { ProfileLoading } from "./ProfileLoading";
99

10-
export const metadata: Metadata = { title: "Profile - AutoGPT Platform" };
10+
export default function UserProfilePage() {
11+
const { user } = useSupabase();
1112

12-
export default async function UserProfilePage(): Promise<React.ReactElement> {
13-
const api = new BackendAPI();
14-
const profile = await api.getStoreProfile().catch((error) => {
15-
console.error("Error fetching profile:", error);
16-
return null;
13+
const {
14+
data: profile,
15+
isLoading,
16+
isError,
17+
error,
18+
refetch,
19+
} = useGetV2GetUserProfile<ProfileDetails | null>({
20+
query: {
21+
enabled: !!user,
22+
select: (res) => {
23+
if (res.status === 200) {
24+
return {
25+
...res.data,
26+
avatar_url: res.data.avatar_url ?? "",
27+
};
28+
}
29+
return null;
30+
},
31+
},
1732
});
1833

19-
if (!profile) {
20-
redirect("/login");
34+
if (isError) {
35+
return (
36+
<div className="flex flex-col items-center justify-center px-4">
37+
<ErrorCard
38+
responseError={
39+
error
40+
? {
41+
detail: error.detail,
42+
}
43+
: undefined
44+
}
45+
context="profile"
46+
onRetry={() => {
47+
void refetch();
48+
}}
49+
/>
50+
</div>
51+
);
52+
}
53+
54+
if (isLoading || !user || !profile) {
55+
return <ProfileLoading />;
2156
}
2257

2358
return (

autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

3-
import { Separator } from "@/components/__legacy__/ui/separator";
43
import { NotificationPreference } from "@/app/api/__generated__/models/notificationPreference";
4+
import { Separator } from "@/components/__legacy__/ui/separator";
55
import { User } from "@supabase/supabase-js";
66
import { EmailForm } from "./components/EmailForm/EmailForm";
77
import { NotificationForm } from "./components/NotificationForm/NotificationForm";
@@ -18,6 +18,8 @@ export function SettingsForm({
1818
user,
1919
timezone,
2020
}: SettingsFormProps) {
21+
if (!user || !preferences) return null;
22+
2123
return (
2224
<div className="flex flex-col gap-8">
2325
<EmailForm user={user} />

autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/TimezoneForm/TimezoneForm.tsx

Lines changed: 16 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,11 @@
11
"use client";
22

3-
import * as React from "react";
4-
import { useTimezoneForm } from "./useTimezoneForm";
5-
import { User } from "@supabase/supabase-js";
63
import {
74
Card,
85
CardContent,
96
CardHeader,
107
CardTitle,
118
} from "@/components/__legacy__/ui/card";
12-
import { Button } from "@/components/atoms/Button/Button";
13-
import {
14-
Select,
15-
SelectContent,
16-
SelectItem,
17-
SelectTrigger,
18-
SelectValue,
19-
} from "@/components/__legacy__/ui/select";
209
import {
2110
Form,
2211
FormControl,
@@ -25,48 +14,26 @@ import {
2514
FormLabel,
2615
FormMessage,
2716
} from "@/components/__legacy__/ui/form";
17+
import {
18+
Select,
19+
SelectContent,
20+
SelectItem,
21+
SelectTrigger,
22+
SelectValue,
23+
} from "@/components/__legacy__/ui/select";
24+
import { Button } from "@/components/atoms/Button/Button";
25+
import { User } from "@supabase/supabase-js";
26+
import * as React from "react";
27+
import { TIMEZONES } from "./helpers";
28+
import { useTimezoneForm } from "./useTimezoneForm";
2829

29-
type TimezoneFormProps = {
30+
type Props = {
3031
user: User;
3132
currentTimezone?: string;
3233
};
3334

34-
// Common timezones list - can be expanded later
35-
const TIMEZONES = [
36-
{ value: "UTC", label: "UTC (Coordinated Universal Time)" },
37-
{ value: "America/New_York", label: "Eastern Time (US & Canada)" },
38-
{ value: "America/Chicago", label: "Central Time (US & Canada)" },
39-
{ value: "America/Denver", label: "Mountain Time (US & Canada)" },
40-
{ value: "America/Los_Angeles", label: "Pacific Time (US & Canada)" },
41-
{ value: "America/Phoenix", label: "Arizona (US)" },
42-
{ value: "America/Anchorage", label: "Alaska (US)" },
43-
{ value: "Pacific/Honolulu", label: "Hawaii (US)" },
44-
{ value: "Europe/London", label: "London (UK)" },
45-
{ value: "Europe/Paris", label: "Paris (France)" },
46-
{ value: "Europe/Berlin", label: "Berlin (Germany)" },
47-
{ value: "Europe/Moscow", label: "Moscow (Russia)" },
48-
{ value: "Asia/Dubai", label: "Dubai (UAE)" },
49-
{ value: "Asia/Kolkata", label: "India Standard Time" },
50-
{ value: "Asia/Shanghai", label: "China Standard Time" },
51-
{ value: "Asia/Tokyo", label: "Tokyo (Japan)" },
52-
{ value: "Asia/Seoul", label: "Seoul (South Korea)" },
53-
{ value: "Asia/Singapore", label: "Singapore" },
54-
{ value: "Australia/Sydney", label: "Sydney (Australia)" },
55-
{ value: "Australia/Melbourne", label: "Melbourne (Australia)" },
56-
{ value: "Pacific/Auckland", label: "Auckland (New Zealand)" },
57-
{ value: "America/Toronto", label: "Toronto (Canada)" },
58-
{ value: "America/Vancouver", label: "Vancouver (Canada)" },
59-
{ value: "America/Mexico_City", label: "Mexico City (Mexico)" },
60-
{ value: "America/Sao_Paulo", label: "São Paulo (Brazil)" },
61-
{ value: "America/Buenos_Aires", label: "Buenos Aires (Argentina)" },
62-
{ value: "Africa/Cairo", label: "Cairo (Egypt)" },
63-
{ value: "Africa/Johannesburg", label: "Johannesburg (South Africa)" },
64-
];
65-
66-
export function TimezoneForm({
67-
user,
68-
currentTimezone = "not-set",
69-
}: TimezoneFormProps) {
35+
export function TimezoneForm({ user, currentTimezone = "not-set" }: Props) {
36+
console.log("currentTimezone", currentTimezone);
7037
// If timezone is not set, try to detect it from the browser
7138
const effectiveTimezone = React.useMemo(() => {
7239
if (currentTimezone === "not-set") {
@@ -120,7 +87,7 @@ export function TimezoneForm({
12087
</FormItem>
12188
)}
12289
/>
123-
<Button type="submit" disabled={isLoading}>
90+
<Button type="submit" disabled={isLoading} size="small">
12491
{isLoading ? "Saving..." : "Save timezone"}
12592
</Button>
12693
</form>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
export const TIMEZONES = [
2+
{ value: "UTC", label: "UTC (Coordinated Universal Time)" },
3+
{ value: "America/Adak", label: "Adak (US - Aleutian Islands)" },
4+
{ value: "America/Anchorage", label: "Anchorage (US - Alaska)" },
5+
{ value: "America/Los_Angeles", label: "Los Angeles (US - Pacific)" },
6+
{ value: "America/Tijuana", label: "Tijuana (Mexico - Pacific)" },
7+
{ value: "America/Phoenix", label: "Phoenix (US - Arizona)" },
8+
{ value: "America/Denver", label: "Denver (US - Mountain)" },
9+
{ value: "America/Chicago", label: "Chicago (US - Central)" },
10+
{ value: "America/New_York", label: "New York (US - Eastern)" },
11+
{ value: "America/Toronto", label: "Toronto (Canada - Eastern)" },
12+
{ value: "America/Halifax", label: "Halifax (Canada - Atlantic)" },
13+
{ value: "America/St_Johns", label: "St. John's (Canada - Newfoundland)" },
14+
{ value: "America/Caracas", label: "Caracas (Venezuela)" },
15+
{ value: "America/Bogota", label: "Bogotá (Colombia)" },
16+
{ value: "America/Lima", label: "Lima (Peru)" },
17+
{ value: "America/Santiago", label: "Santiago (Chile)" },
18+
{ value: "America/La_Paz", label: "La Paz (Bolivia)" },
19+
{ value: "America/Asuncion", label: "Asunción (Paraguay)" },
20+
{ value: "America/Montevideo", label: "Montevideo (Uruguay)" },
21+
{ value: "America/Buenos_Aires", label: "Buenos Aires (Argentina)" },
22+
{ value: "America/Sao_Paulo", label: "São Paulo (Brazil)" },
23+
{ value: "America/Manaus", label: "Manaus (Brazil - Amazon)" },
24+
{ value: "America/Fortaleza", label: "Fortaleza (Brazil - Northeast)" },
25+
{ value: "America/Mexico_City", label: "Mexico City (Mexico)" },
26+
{ value: "America/Guatemala", label: "Guatemala" },
27+
{ value: "America/Costa_Rica", label: "Costa Rica" },
28+
{ value: "America/Panama", label: "Panama" },
29+
{ value: "America/Havana", label: "Havana (Cuba)" },
30+
{ value: "America/Jamaica", label: "Jamaica" },
31+
{ value: "America/Port-au-Prince", label: "Port-au-Prince (Haiti)" },
32+
{
33+
value: "America/Santo_Domingo",
34+
label: "Santo Domingo (Dominican Republic)",
35+
},
36+
{ value: "America/Puerto_Rico", label: "Puerto Rico" },
37+
{ value: "Atlantic/Azores", label: "Azores (Portugal)" },
38+
{ value: "Atlantic/Cape_Verde", label: "Cape Verde" },
39+
{ value: "Europe/London", label: "London (UK)" },
40+
{ value: "Europe/Dublin", label: "Dublin (Ireland)" },
41+
{ value: "Europe/Lisbon", label: "Lisbon (Portugal)" },
42+
{ value: "Europe/Madrid", label: "Madrid (Spain)" },
43+
{ value: "Europe/Paris", label: "Paris (France)" },
44+
{ value: "Europe/Brussels", label: "Brussels (Belgium)" },
45+
{ value: "Europe/Amsterdam", label: "Amsterdam (Netherlands)" },
46+
{ value: "Europe/Berlin", label: "Berlin (Germany)" },
47+
{ value: "Europe/Rome", label: "Rome (Italy)" },
48+
{ value: "Europe/Vienna", label: "Vienna (Austria)" },
49+
{ value: "Europe/Zurich", label: "Zurich (Switzerland)" },
50+
{ value: "Europe/Prague", label: "Prague (Czech Republic)" },
51+
{ value: "Europe/Warsaw", label: "Warsaw (Poland)" },
52+
{ value: "Europe/Stockholm", label: "Stockholm (Sweden)" },
53+
{ value: "Europe/Oslo", label: "Oslo (Norway)" },
54+
{ value: "Europe/Copenhagen", label: "Copenhagen (Denmark)" },
55+
{ value: "Europe/Helsinki", label: "Helsinki (Finland)" },
56+
{ value: "Europe/Athens", label: "Athens (Greece)" },
57+
{ value: "Europe/Bucharest", label: "Bucharest (Romania)" },
58+
{ value: "Europe/Sofia", label: "Sofia (Bulgaria)" },
59+
{ value: "Europe/Budapest", label: "Budapest (Hungary)" },
60+
{ value: "Europe/Belgrade", label: "Belgrade (Serbia)" },
61+
{ value: "Europe/Zagreb", label: "Zagreb (Croatia)" },
62+
{ value: "Europe/Moscow", label: "Moscow (Russia)" },
63+
{ value: "Europe/Kiev", label: "Kyiv (Ukraine)" },
64+
{ value: "Europe/Istanbul", label: "Istanbul (Turkey)" },
65+
{ value: "Asia/Dubai", label: "Dubai (UAE)" },
66+
{ value: "Asia/Muscat", label: "Muscat (Oman)" },
67+
{ value: "Asia/Kuwait", label: "Kuwait" },
68+
{ value: "Asia/Riyadh", label: "Riyadh (Saudi Arabia)" },
69+
{ value: "Asia/Baghdad", label: "Baghdad (Iraq)" },
70+
{ value: "Asia/Tehran", label: "Tehran (Iran)" },
71+
{ value: "Asia/Kabul", label: "Kabul (Afghanistan)" },
72+
{ value: "Asia/Karachi", label: "Karachi (Pakistan)" },
73+
{ value: "Asia/Tashkent", label: "Tashkent (Uzbekistan)" },
74+
{ value: "Asia/Dhaka", label: "Dhaka (Bangladesh)" },
75+
{ value: "Asia/Kolkata", label: "Kolkata (India)" },
76+
{ value: "Asia/Kathmandu", label: "Kathmandu (Nepal)" },
77+
{ value: "Asia/Colombo", label: "Colombo (Sri Lanka)" },
78+
{ value: "Asia/Yangon", label: "Yangon (Myanmar)" },
79+
{ value: "Asia/Bangkok", label: "Bangkok (Thailand)" },
80+
{ value: "Asia/Ho_Chi_Minh", label: "Ho Chi Minh City (Vietnam)" },
81+
{ value: "Asia/Jakarta", label: "Jakarta (Indonesia - Western)" },
82+
{ value: "Asia/Makassar", label: "Makassar (Indonesia - Central)" },
83+
{ value: "Asia/Jayapura", label: "Jayapura (Indonesia - Eastern)" },
84+
{ value: "Asia/Manila", label: "Manila (Philippines)" },
85+
{ value: "Asia/Singapore", label: "Singapore" },
86+
{ value: "Asia/Kuala_Lumpur", label: "Kuala Lumpur (Malaysia)" },
87+
{ value: "Asia/Hong_Kong", label: "Hong Kong" },
88+
{ value: "Asia/Shanghai", label: "Shanghai (China)" },
89+
{ value: "Asia/Taipei", label: "Taipei (Taiwan)" },
90+
{ value: "Asia/Seoul", label: "Seoul (South Korea)" },
91+
{ value: "Asia/Tokyo", label: "Tokyo (Japan)" },
92+
{ value: "Asia/Vladivostok", label: "Vladivostok (Russia)" },
93+
{ value: "Asia/Yakutsk", label: "Yakutsk (Russia)" },
94+
{ value: "Asia/Irkutsk", label: "Irkutsk (Russia)" },
95+
{ value: "Asia/Yekaterinburg", label: "Yekaterinburg (Russia)" },
96+
{ value: "Australia/Perth", label: "Perth (Australia - Western)" },
97+
{ value: "Australia/Darwin", label: "Darwin (Australia - Northern)" },
98+
{ value: "Australia/Adelaide", label: "Adelaide (Australia - Central)" },
99+
{ value: "Australia/Brisbane", label: "Brisbane (Australia - Eastern)" },
100+
{ value: "Australia/Sydney", label: "Sydney (Australia - Eastern)" },
101+
{ value: "Australia/Melbourne", label: "Melbourne (Australia - Eastern)" },
102+
{ value: "Australia/Hobart", label: "Hobart (Australia - Eastern)" },
103+
{ value: "Pacific/Auckland", label: "Auckland (New Zealand)" },
104+
{ value: "Pacific/Fiji", label: "Fiji" },
105+
{ value: "Pacific/Guam", label: "Guam" },
106+
{ value: "Pacific/Honolulu", label: "Honolulu (US - Hawaii)" },
107+
{ value: "Pacific/Samoa", label: "Samoa" },
108+
{ value: "Pacific/Tahiti", label: "Tahiti (French Polynesia)" },
109+
{ value: "Africa/Cairo", label: "Cairo (Egypt)" },
110+
{ value: "Africa/Johannesburg", label: "Johannesburg (South Africa)" },
111+
{ value: "Africa/Lagos", label: "Lagos (Nigeria)" },
112+
{ value: "Africa/Nairobi", label: "Nairobi (Kenya)" },
113+
{ value: "Africa/Casablanca", label: "Casablanca (Morocco)" },
114+
{ value: "Africa/Algiers", label: "Algiers (Algeria)" },
115+
{ value: "Africa/Tunis", label: "Tunis (Tunisia)" },
116+
{ value: "Africa/Addis_Ababa", label: "Addis Ababa (Ethiopia)" },
117+
{ value: "Africa/Dar_es_Salaam", label: "Dar es Salaam (Tanzania)" },
118+
{ value: "Africa/Kampala", label: "Kampala (Uganda)" },
119+
{ value: "Africa/Khartoum", label: "Khartoum (Sudan)" },
120+
{ value: "Africa/Accra", label: "Accra (Ghana)" },
121+
{ value: "Africa/Abidjan", label: "Abidjan (Ivory Coast)" },
122+
{ value: "Africa/Dakar", label: "Dakar (Senegal)" },
123+
];

0 commit comments

Comments
 (0)