diff --git a/dist.zip b/dist.zip deleted file mode 100644 index 08930d2..0000000 Binary files a/dist.zip and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 815451e..3f13ceb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,15 @@ "@astrojs/react": "^4.2.1", "@astrojs/tailwind": "^6.0.0", "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-select": "^2.1.6", "@shadcn/ui": "^0.0.4", + "@types/react": "^19.0.12", "astro": "^5.5.2", "autoprefixer": "^10.4.21", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.484.0", + "pocketbase": "^0.25.2", "postcss": "^8.5.3", "tailwind-merge": "^3.0.2", "tailwindcss": "^3.4.17", @@ -845,6 +848,44 @@ "node": ">=18" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -1362,12 +1403,67 @@ "node": ">=14" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==", + "license": "MIT" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", "license": "MIT" }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", + "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", + "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", @@ -1434,6 +1530,21 @@ } } }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-dismissable-layer": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", @@ -1519,6 +1630,38 @@ } } }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", + "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-portal": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", @@ -1590,6 +1733,49 @@ } } }, + "node_modules/@radix-ui/react-select": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.6.tgz", + "integrity": "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", @@ -1674,6 +1860,86 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz", + "integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "license": "MIT" + }, "node_modules/@rollup/pluginutils": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", @@ -2145,11 +2411,10 @@ } }, "node_modules/@types/react": { - "version": "19.0.11", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.11.tgz", - "integrity": "sha512-vrdxRZfo9ALXth6yPfV16PYTLZwsUWhVjjC+DkfE5t1suNSbBrWC9YqSuuxJZ8Ps6z1o2ycRpIqzZJIgklq4Tw==", + "version": "19.0.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz", + "integrity": "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -2966,8 +3231,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", @@ -5448,6 +5712,12 @@ "node": ">= 6" } }, + "node_modules/pocketbase": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.25.2.tgz", + "integrity": "sha512-ONZl1+qHJMnhR2uacBlBJ90lm7njtL/zy0606+1ROfK9hSL4LRBRc8r89rMcNRzPzRqCNyoFTh2Qg/lYXdEC1w==", + "license": "MIT" + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", diff --git a/package.json b/package.json index c7041ee..aba8c08 100644 --- a/package.json +++ b/package.json @@ -13,12 +13,15 @@ "@astrojs/react": "^4.2.1", "@astrojs/tailwind": "^6.0.0", "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-select": "^2.1.6", "@shadcn/ui": "^0.0.4", + "@types/react": "^19.0.12", "astro": "^5.5.2", "autoprefixer": "^10.4.21", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.484.0", + "pocketbase": "^0.25.2", "postcss": "^8.5.3", "tailwind-merge": "^3.0.2", "tailwindcss": "^3.4.17", diff --git a/public/assets/eye-close.svg b/public/assets/eye-close.svg new file mode 100644 index 0000000..3f35f8a --- /dev/null +++ b/public/assets/eye-close.svg @@ -0,0 +1 @@ + diff --git a/public/assets/eye.svg b/public/assets/eye.svg new file mode 100644 index 0000000..14c7300 --- /dev/null +++ b/public/assets/eye.svg @@ -0,0 +1 @@ + diff --git a/public/assets/facebook.svg b/public/assets/facebook.svg new file mode 100644 index 0000000..13c1cb7 --- /dev/null +++ b/public/assets/facebook.svg @@ -0,0 +1 @@ + diff --git a/public/assets/github.svg b/public/assets/github.svg new file mode 100644 index 0000000..18f7a45 --- /dev/null +++ b/public/assets/github.svg @@ -0,0 +1 @@ + diff --git a/public/assets/google.svg b/public/assets/google.svg new file mode 100644 index 0000000..6cc6fec --- /dev/null +++ b/public/assets/google.svg @@ -0,0 +1 @@ + diff --git a/src/components/Login.tsx b/src/components/Login.tsx new file mode 100644 index 0000000..16828d7 --- /dev/null +++ b/src/components/Login.tsx @@ -0,0 +1,266 @@ +import { useState } from 'react'; +import type { FormEvent } from 'react'; +import PocketBase from 'pocketbase'; +import { Input } from "./ui/input"; +import { Button } from "./ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"; +import { Eye, EyeOff, Loader2 } from "lucide-react"; +import { Separator } from "./ui/separator"; +import { Label } from "./ui/label"; + +interface AuthStatus { + message: string; + isError: boolean; +} + +interface UserRecord { + id: string; + email: string; + name?: string; + avatar?: string; + [key: string]: any; +} + +interface AuthResponse { + token: string; + record: UserRecord; +} + +const LoginPage = () => { + const [email, setEmail] = useState('suvodip@siliconpin.com'); + const [password, setPassword] = useState('Simple2pass'); + const [passwordVisible, setPasswordVisible] = useState(false); + const [status, setStatus] = useState({ message: '', isError: false }); + const [isLoading, setIsLoading] = useState(false); + + const pb = new PocketBase("https://tst-pb.s38.siliconpin.com"); + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + setIsLoading(true); + setStatus({ message: '', isError: false }); + + try { + const authData = await pb.collection("users").authWithPassword(email, password); + + const authResponse: AuthResponse = { + token: authData.token, + record: { + query: 'new', + id: authData.record.id, + email: authData.record.email, + name: authData.record.name || '', + avatar: authData.record.avatar || '' + } + }; + + await syncSessionWithBackend(authResponse); + // window.location.href = '/profile'; + } catch (error) { + console.error("Login failed:", error); + setStatus({ + message: "Login failed. Please check your credentials.", + isError: true + }); + } finally { + setIsLoading(false); + } + }; + + const loginWithOAuth2 = async (provider: 'google' | 'facebook' | 'github') => { + try { + setIsLoading(true); + setStatus({ message: '', isError: false }); + + const authData = await pb.collection('users').authWithOAuth2({ provider }); + + if (!authData?.record) { + throw new Error("No user record found"); + } + + const authResponse: AuthResponse = { + token: authData.token, + record: { + query: 'new', + id: authData.record.id, + email: authData.record.email || '', + name: authData.record.name || '', + avatar: authData.record.avatar || '' + } + }; + + await syncSessionWithBackend(authResponse); + // window.location.href = '/profile'; + } catch (error) { + console.error(`${provider} Login failed:`, error); + setStatus({ + message: `${provider} login failed. Please try again.`, + isError: true + }); + } finally { + setIsLoading(false); + } + }; + + const syncSessionWithBackend = async (authData: AuthResponse) => { + try { + const response = await fetch('http://localhost:2058/host-api/v1/users/session/', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query: 'new', + accessToken: authData.token, + email: authData.record.email, + name: authData.record.name, + avatar: authData.record.avatar + ? pb.files.getUrl(authData.record, authData.record.avatar) + : '', + isAuthenticated: true, + id: authData.record.id + }) + }); + + if (!response.ok) { + throw new Error('Failed to sync session'); + } + + const data = await response.json(); + console.log('Session synced with backend:', data); + } catch (error) { + console.error('Error syncing session:', error); + } + }; + + return ( +
+ + + Welcome Back + + Sign in to access your account + + + + + {status.message && ( +
+ {status.message} +
+ )} + +
+
+ + setEmail(e.target.value)} + placeholder="you@example.com" + required + className="focus:ring-2 focus:ring-blue-500" + /> +
+ +
+
+ + +
+
+ setPassword(e.target.value)} + placeholder="••••••••" + required + className="focus:ring-2 focus:ring-blue-500 pr-10" + /> + +
+
+ + +
+ +
+
+ +
+
+ + Or continue with + +
+
+ +
+ + + +
+
+ +
+ Don't have an account?{' '} + + Sign up + +
+
+
+ ); +}; + +export default LoginPage; \ No newline at end of file diff --git a/src/components/UserProfile.tsx b/src/components/UserProfile.tsx new file mode 100644 index 0000000..744f0d2 --- /dev/null +++ b/src/components/UserProfile.tsx @@ -0,0 +1,210 @@ +import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; +import { Button } from "./ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "./ui/card"; +import { Input } from "./ui/input"; +import { Label } from "./ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "./ui/select"; +import { Separator } from "./ui/separator"; +import { Textarea } from "./ui/textarea"; +import React, { useState, useEffect } from 'react'; + +interface SessionData { + [key: string]: any; +} + +interface UserData { + success: boolean; + session_data: SessionData; + user_avatar: string; +} + + +export default function ProfilePage() { + const [userData, setUserData] = useState(null); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchData = async () => { + try { + const response = await fetch( + 'http://localhost:2058/host-api/v1/users/get-profile-data/', + { + credentials: 'include', + headers: { + 'Accept': 'application/json', + } + } + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data: UserData = await response.json(); + // console.log('success message', data.success); + if(data.success === true){ + setUserData(data); + // console.log('User Data', data); + } + + } catch (error) { + console.error('Fetch error:', error); + setError(error instanceof Error ? error.message : 'An unknown error occurred'); + } + }; + + fetchData(); + }, []); + + if (error) { + return
Error: {error}
; + } + + if (!userData) { + return
Loading profile data...
; + } + return ( +
+
+

Profile

+

+ Update your profile settings. +

+
+ +
+
+ + + Personal Information + + Update your personal information and avatar. + + + +
+ + + JP + +
+ +

+ JPG, GIF or PNG. 1MB max. +

+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ +