diff --git a/shop/app/account-kunden/AccountSettingsForm.tsx b/shop/app/account-kunden/AccountSettingsForm.tsx new file mode 100644 index 0000000..bd42cd7 --- /dev/null +++ b/shop/app/account-kunden/AccountSettingsForm.tsx @@ -0,0 +1,346 @@ +'use client'; + +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { updateUserProfile, updateCompanyDetails } from '@/lib/actions/users'; +import { updatePassword } from '@/lib/actions/auth'; +import { Save, KeyRound, Loader2 } from 'lucide-react'; + +interface AccountSettingsFormProps { + userId: string; + profile: any; + company: any; +} + +export function AccountSettingsForm({ userId, profile, company }: AccountSettingsFormProps) { + // Profil State + const [firstName, setFirstName] = useState(profile?.first_name || ''); + const [lastName, setLastName] = useState(profile?.last_name || ''); + const [profileMessage, setProfileMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); + const [isUpdatingProfile, setIsUpdatingProfile] = useState(false); + + // Company State + const [street, setStreet] = useState(company?.street || ''); + const [zip, setZip] = useState(company?.zip || ''); + const [city, setCity] = useState(company?.city || ''); + const [companyEmail, setCompanyEmail] = useState(company?.email || ''); + const [companyMessage, setCompanyMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); + const [isUpdatingCompany, setIsUpdatingCompany] = useState(false); + + // Passwort State + const [newPassword, setNewPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [passwordMessage, setPasswordMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); + const [isUpdatingPassword, setIsUpdatingPassword] = useState(false); + + // Profil speichern + const handleUpdateProfile = async (e: React.FormEvent) => { + e.preventDefault(); + setProfileMessage(null); + setIsUpdatingProfile(true); + + try { + const res = await updateUserProfile(userId, { + first_name: firstName || null, + last_name: lastName || null, + }); + + if (res.success) { + setProfileMessage({ type: 'success', text: 'Persönliche Daten erfolgreich aktualisiert.' }); + } else { + setProfileMessage({ type: 'error', text: res.error || 'Fehler beim Aktualisieren des Profils.' }); + } + } catch (err: any) { + setProfileMessage({ type: 'error', text: err.message || 'Ein unerwarteter Fehler ist aufgetreten.' }); + } finally { + setIsUpdatingProfile(false); + } + }; + + // Company speichern + const handleUpdateCompany = async (e: React.FormEvent) => { + e.preventDefault(); + if (!company?.id) return; + setCompanyMessage(null); + setIsUpdatingCompany(true); + + try { + const res = await updateCompanyDetails(company.id, { + street: street || null, + zip: zip || null, + city: city || null, + email: companyEmail || null, + }); + + if (res.success) { + setCompanyMessage({ type: 'success', text: 'Unternehmensdaten erfolgreich aktualisiert.' }); + } else { + setCompanyMessage({ type: 'error', text: res.error || 'Fehler beim Aktualisieren der Unternehmensdaten.' }); + } + } catch (err: any) { + setCompanyMessage({ type: 'error', text: err.message || 'Ein unerwarteter Fehler ist aufgetreten.' }); + } finally { + setIsUpdatingCompany(false); + } + }; + + // Passwort speichern + const handleUpdatePassword = async (e: React.FormEvent) => { + e.preventDefault(); + setPasswordMessage(null); + + if (newPassword.length < 6) { + setPasswordMessage({ type: 'error', text: 'Das Passwort muss mindestens 6 Zeichen lang sein.' }); + return; + } + + if (newPassword !== confirmPassword) { + setPasswordMessage({ type: 'error', text: 'Die Passwörter stimmen nicht überein.' }); + return; + } + + setIsUpdatingPassword(true); + + try { + const res = await updatePassword(newPassword); + if (res.success) { + setPasswordMessage({ type: 'success', text: 'Passwort erfolgreich geändert.' }); + setNewPassword(''); + setConfirmPassword(''); + } else { + setPasswordMessage({ type: 'error', text: res.error || 'Fehler beim Ändern des Passworts.' }); + } + } catch (err: any) { + setPasswordMessage({ type: 'error', text: err.message || 'Ein unerwarteter Fehler ist aufgetreten.' }); + } finally { + setIsUpdatingPassword(false); + } + }; + + return ( +
+ {/* 1. Persönliche Daten */} + + + Persönliche Daten + + Aktualisieren Sie Ihren Vor- und Nachnamen. + + + +
+
+
+ + setFirstName(e.target.value)} + className="bg-slate-950 border-white/10 focus:border-primary text-white" + placeholder="Max" + /> +
+
+ + setLastName(e.target.value)} + className="bg-slate-950 border-white/10 focus:border-primary text-white" + placeholder="Mustermann" + /> +
+
+ + {profileMessage && ( +
+ {profileMessage.text} +
+ )} + +
+ +
+
+
+
+ + {/* 2. Unternehmensdaten */} + {company && ( + + + Unternehmensdaten + + Verwalten Sie die Adressdaten Ihres Partner-Unternehmens. + + + +
+
+
+ + +
+
+ + setStreet(e.target.value)} + className="bg-slate-950 border-white/10 focus:border-primary text-white" + placeholder="Musterstraße 12" + /> +
+
+ + setZip(e.target.value)} + className="bg-slate-950 border-white/10 focus:border-primary text-white" + placeholder="12345" + /> +
+
+ + setCity(e.target.value)} + className="bg-slate-950 border-white/10 focus:border-primary text-white" + placeholder="Musterstadt" + /> +
+
+ + setCompanyEmail(e.target.value)} + className="bg-slate-950 border-white/10 focus:border-primary text-white" + placeholder="partner@firma.de" + /> +
+
+ + {companyMessage && ( +
+ {companyMessage.text} +
+ )} + +
+ +
+
+
+
+ )} + + {/* 3. Sicherheit / Passwort ändern */} + + + Passwort ändern + + Geben Sie ein neues Passwort für Ihren Zugang ein. + + + +
+
+
+ + setNewPassword(e.target.value)} + className="bg-slate-950 border-white/10 focus:border-primary text-white" + placeholder="••••••••" + /> +
+
+ + setConfirmPassword(e.target.value)} + className="bg-slate-950 border-white/10 focus:border-primary text-white" + placeholder="••••••••" + /> +
+
+ + {passwordMessage && ( +
+ {passwordMessage.text} +
+ )} + +
+ +
+
+
+
+
+ ); +} diff --git a/shop/app/account-kunden/page.tsx b/shop/app/account-kunden/page.tsx new file mode 100644 index 0000000..d230bb1 --- /dev/null +++ b/shop/app/account-kunden/page.tsx @@ -0,0 +1,131 @@ +import { createClient } from '@/lib/supabase/server'; +import { redirect } from 'next/navigation'; +import Link from 'next/link'; +import { ArrowLeft, User as UserIcon, Building2, ShieldAlert } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; +import { AccountSettingsForm } from './AccountSettingsForm'; + +export default async function AccountKundenPage() { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); + + if (!user) { + redirect('/auth/login?next=/account-kunden'); + } + + // 1. Hole User aus custom users Tabelle + const { data: dbUser } = await supabase + .from('users') + .select('role, company_id') + .eq('id', user.id) + .single(); + + // 2. Hole Profil-Daten + const { data: profile } = await supabase + .from('profiles') + .select('*') + .eq('id', user.id) + .single(); + + // 3. Hole Company-Daten falls vorhanden + let company = null; + if (dbUser?.company_id) { + const { data: companyData } = await supabase + .from('companies') + .select('*') + .eq('id', dbUser.company_id) + .single(); + company = companyData; + } + + return ( +
+
+ + {/* Header */} +
+ + + +
+

+ + Account Einstellungen +

+

+ Verwalten Sie Ihre persönlichen Profildaten, Unternehmensdaten und Sicherheitseinstellungen. +

+
+
+ + {/* Dashboard Grid */} +
+ + {/* Linke Infospalte */} +
+ + + + + Benutzer-Info + + + Ihr aktueller Systemstatus + + + +
+ E-Mail-Adresse + {user.email} +
+
+ Rolle + + {dbUser?.role || 'partner'} + +
+ {company && ( +
+ Unternehmen + + + {company.name} + +
+ )} +
+
+ + {!dbUser?.company_id && ( + + + +
+

Kein Unternehmen zugewiesen

+

+ Wenden Sie sich an einen Administrator, um Ihrem Account ein Partner-Unternehmen zuzuweisen. +

+
+
+
+ )} +
+ + {/* Rechte Formularspalte */} +
+ +
+ +
+ +
+
+ ); +} diff --git a/shop/lib/actions/users.ts b/shop/lib/actions/users.ts index deea5ba..eb5d789 100644 --- a/shop/lib/actions/users.ts +++ b/shop/lib/actions/users.ts @@ -1,6 +1,7 @@ 'use server'; import { createAdminClient } from '@/lib/supabase/admin'; +import { createClient } from '@/lib/supabase/server'; import { revalidatePath, unstable_noStore as noStore } from 'next/cache'; import { sendMail } from '@/utils/mail'; @@ -262,6 +263,51 @@ export async function updateUserProfile(id: string, data: { first_name: string | } } +export async function updateCompanyDetails(companyId: string, data: { street: string | null; zip: string | null; city: string | null; email: string | null }) { + try { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); + if (!user) { + return { success: false, error: 'Nicht authentifiziert.' }; + } + + // Berechtigung prüfen: Ist der User Mitglied dieser Company oder Admin? + const { data: dbUser } = await supabase + .from('users') + .select('role, company_id') + .eq('id', user.id) + .single(); + + if (!dbUser) { + return { success: false, error: 'Benutzer nicht gefunden.' }; + } + + if (dbUser.role !== 'admin' && dbUser.company_id !== companyId) { + return { success: false, error: 'Keine Berechtigung zur Bearbeitung dieses Unternehmens.' }; + } + + const admin = createAdminClient(); + const { error } = await admin + .from('companies') + .update({ + street: data.street, + zip: data.zip, + city: data.city, + email: data.email + }) + .eq('id', companyId); + + if (error) { + return { success: false, error: error.message }; + } + + return { success: true }; + } catch (err: any) { + console.error('updateCompanyDetails Server Action failed:', err); + return { success: false, error: err.message || 'Unternehmen konnte nicht aktualisiert werden.' }; + } +} + function getBeautifulEmailHtml(title: string, messageHtml: string, buttonText?: string, buttonUrl?: string) { return `