From d6fc2e43dc1d8e607cfc522128d27bc4ad2814b6 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Tue, 27 Jan 2026 23:07:04 +0000 Subject: [PATCH] Changes --- src/App.tsx | 2 + src/components/AccountMenu.tsx | 13 ++- src/pages/Profile.tsx | 204 +++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 src/pages/Profile.tsx diff --git a/src/App.tsx b/src/App.tsx index acc0c71..42cdfac 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,6 +6,7 @@ import { BrowserRouter, Routes, Route } from "react-router-dom"; import Index from "./pages/Index"; import NotFound from "./pages/NotFound"; import ResetPassword from "./pages/ResetPassword"; +import Profile from "./pages/Profile"; import { useCartSync } from "./hooks/useCartSync"; const queryClient = new QueryClient(); @@ -18,6 +19,7 @@ function AppContent() { } /> } /> + } /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} } /> diff --git a/src/components/AccountMenu.tsx b/src/components/AccountMenu.tsx index b3d7e46..b8ab540 100644 --- a/src/components/AccountMenu.tsx +++ b/src/components/AccountMenu.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; -import { LogIn, LogOut } from "lucide-react"; +import { useNavigate } from "react-router-dom"; +import { LogOut, User } from "lucide-react"; import { DropdownMenu, DropdownMenuContent, @@ -14,6 +15,7 @@ const AccountMenu = () => { const { user, loading, signOut } = useAuth(); const [authDialogOpen, setAuthDialogOpen] = useState(false); const [authMode, setAuthMode] = useState<"login" | "signup">("login"); + const navigate = useNavigate(); const handleOpenAuth = (mode: "login" | "signup") => { setAuthMode(mode); @@ -49,9 +51,16 @@ const AccountMenu = () => { {user.email} + navigate("/profile")} + className="cursor-pointer flex items-center gap-2 hover:!bg-[rgba(220,38,38,0.3)] focus:!bg-[rgba(220,38,38,0.3)] hover:!text-black focus:!text-black" + > + + Min profil + Logga ut diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx new file mode 100644 index 0000000..2f2b00b --- /dev/null +++ b/src/pages/Profile.tsx @@ -0,0 +1,204 @@ +import { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import { z } from "zod"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { useAuth } from "@/hooks/useAuth"; +import { useToast } from "@/hooks/use-toast"; +import { supabase } from "@/integrations/supabase/client"; +import Header from "@/components/Header"; + +const profileSchema = z.object({ + display_name: z.string().trim().max(100, { message: "Namn får max vara 100 tecken" }).optional(), + phone: z.string().trim().max(20, { message: "Telefonnummer får max vara 20 tecken" }).optional(), + address: z.string().trim().max(500, { message: "Adress får max vara 500 tecken" }).optional(), +}); + +interface ProfileData { + display_name: string; + phone: string; + address: string; +} + +const Profile = () => { + const [profile, setProfile] = useState({ + display_name: "", + phone: "", + address: "", + }); + const [isLoading, setIsLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const { user, loading: authLoading } = useAuth(); + const { toast } = useToast(); + const navigate = useNavigate(); + + useEffect(() => { + if (!authLoading && !user) { + navigate("/"); + return; + } + + if (user) { + fetchProfile(); + } + }, [user, authLoading, navigate]); + + const fetchProfile = async () => { + try { + const { data, error } = await supabase + .from("profiles") + .select("display_name, phone, address") + .eq("user_id", user!.id) + .maybeSingle(); + + if (error) { + console.error("Error fetching profile:", error); + toast({ + title: "Kunde inte hämta profil", + description: error.message, + variant: "destructive", + }); + } else if (data) { + setProfile({ + display_name: data.display_name || "", + phone: data.phone || "", + address: data.address || "", + }); + } + } finally { + setIsLoading(false); + } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const validation = profileSchema.safeParse(profile); + if (!validation.success) { + toast({ + title: "Fel", + description: validation.error.errors[0].message, + variant: "destructive", + }); + return; + } + + setIsSaving(true); + + try { + const { error } = await supabase + .from("profiles") + .update({ + display_name: profile.display_name || null, + phone: profile.phone || null, + address: profile.address || null, + }) + .eq("user_id", user!.id); + + if (error) { + toast({ + title: "Kunde inte spara profil", + description: error.message, + variant: "destructive", + }); + } else { + toast({ + title: "Profil sparad!", + description: "Dina uppgifter har uppdaterats.", + }); + } + } finally { + setIsSaving(false); + } + }; + + if (authLoading || isLoading) { + return ( +
+
+
+

Laddar...

+
+
+ ); + } + + return ( +
+
+
+
+
+

Min profil

+

+ {user?.email} +

+ +
+
+ + + setProfile({ ...profile, display_name: e.target.value }) + } + placeholder="Ditt namn" + /> +
+ +
+ + + setProfile({ ...profile, phone: e.target.value }) + } + placeholder="070-123 45 67" + /> +
+ +
+ +