feat: Add partner account settings page under /account-kunden
All checks were successful
Staging Build / build (push) Successful in 3m33s
All checks were successful
Staging Build / build (push) Successful in 3m33s
This commit is contained in:
346
shop/app/account-kunden/AccountSettingsForm.tsx
Normal file
346
shop/app/account-kunden/AccountSettingsForm.tsx
Normal file
@@ -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 (
|
||||||
|
<div className="space-y-8">
|
||||||
|
{/* 1. Persönliche Daten */}
|
||||||
|
<Card className="glass-dark border-white/10">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-xl">Persönliche Daten</CardTitle>
|
||||||
|
<CardDescription className="text-slate-400">
|
||||||
|
Aktualisieren Sie Ihren Vor- und Nachnamen.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form onSubmit={handleUpdateProfile} className="space-y-4">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="firstName">Vorname</Label>
|
||||||
|
<Input
|
||||||
|
id="firstName"
|
||||||
|
value={firstName}
|
||||||
|
onChange={(e) => setFirstName(e.target.value)}
|
||||||
|
className="bg-slate-950 border-white/10 focus:border-primary text-white"
|
||||||
|
placeholder="Max"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="lastName">Nachname</Label>
|
||||||
|
<Input
|
||||||
|
id="lastName"
|
||||||
|
value={lastName}
|
||||||
|
onChange={(e) => setLastName(e.target.value)}
|
||||||
|
className="bg-slate-950 border-white/10 focus:border-primary text-white"
|
||||||
|
placeholder="Mustermann"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{profileMessage && (
|
||||||
|
<div
|
||||||
|
className={`p-3 rounded-lg text-sm border ${
|
||||||
|
profileMessage.type === 'success'
|
||||||
|
? 'bg-green-500/10 border-green-500/20 text-green-400'
|
||||||
|
: 'bg-red-500/10 border-red-500/20 text-red-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{profileMessage.text}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit" disabled={isUpdatingProfile} className="gap-2">
|
||||||
|
{isUpdatingProfile ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="w-4 h-4 animate-spin" /> Speichern...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Save className="w-4 h-4" /> Profil speichern
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 2. Unternehmensdaten */}
|
||||||
|
{company && (
|
||||||
|
<Card className="glass-dark border-white/10">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-xl">Unternehmensdaten</CardTitle>
|
||||||
|
<CardDescription className="text-slate-400">
|
||||||
|
Verwalten Sie die Adressdaten Ihres Partner-Unternehmens.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form onSubmit={handleUpdateCompany} className="space-y-4">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-2 sm:col-span-2">
|
||||||
|
<Label htmlFor="companyName">Firmenname (nicht änderbar)</Label>
|
||||||
|
<Input
|
||||||
|
id="companyName"
|
||||||
|
value={company.name}
|
||||||
|
disabled
|
||||||
|
className="bg-slate-950/50 border-white/5 text-slate-400 cursor-not-allowed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 sm:col-span-2">
|
||||||
|
<Label htmlFor="street">Straße & Hausnummer</Label>
|
||||||
|
<Input
|
||||||
|
id="street"
|
||||||
|
value={street}
|
||||||
|
onChange={(e) => setStreet(e.target.value)}
|
||||||
|
className="bg-slate-950 border-white/10 focus:border-primary text-white"
|
||||||
|
placeholder="Musterstraße 12"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="zip">Postleitzahl</Label>
|
||||||
|
<Input
|
||||||
|
id="zip"
|
||||||
|
value={zip}
|
||||||
|
onChange={(e) => setZip(e.target.value)}
|
||||||
|
className="bg-slate-950 border-white/10 focus:border-primary text-white"
|
||||||
|
placeholder="12345"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="city">Ort</Label>
|
||||||
|
<Input
|
||||||
|
id="city"
|
||||||
|
value={city}
|
||||||
|
onChange={(e) => setCity(e.target.value)}
|
||||||
|
className="bg-slate-950 border-white/10 focus:border-primary text-white"
|
||||||
|
placeholder="Musterstadt"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 sm:col-span-2">
|
||||||
|
<Label htmlFor="companyEmail">E-Mail für Rechnungen / Anfragen</Label>
|
||||||
|
<Input
|
||||||
|
id="companyEmail"
|
||||||
|
type="email"
|
||||||
|
value={companyEmail}
|
||||||
|
onChange={(e) => setCompanyEmail(e.target.value)}
|
||||||
|
className="bg-slate-950 border-white/10 focus:border-primary text-white"
|
||||||
|
placeholder="partner@firma.de"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{companyMessage && (
|
||||||
|
<div
|
||||||
|
className={`p-3 rounded-lg text-sm border ${
|
||||||
|
companyMessage.type === 'success'
|
||||||
|
? 'bg-green-500/10 border-green-500/20 text-green-400'
|
||||||
|
: 'bg-red-500/10 border-red-500/20 text-red-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{companyMessage.text}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit" disabled={isUpdatingCompany} className="gap-2">
|
||||||
|
{isUpdatingCompany ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="w-4 h-4 animate-spin" /> Speichern...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Save className="w-4 h-4" /> Firmendaten speichern
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 3. Sicherheit / Passwort ändern */}
|
||||||
|
<Card className="glass-dark border-white/10">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-xl">Passwort ändern</CardTitle>
|
||||||
|
<CardDescription className="text-slate-400">
|
||||||
|
Geben Sie ein neues Passwort für Ihren Zugang ein.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form onSubmit={handleUpdatePassword} className="space-y-4">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="newPassword">Neues Passwort</Label>
|
||||||
|
<Input
|
||||||
|
id="newPassword"
|
||||||
|
type="password"
|
||||||
|
value={newPassword}
|
||||||
|
onChange={(e) => setNewPassword(e.target.value)}
|
||||||
|
className="bg-slate-950 border-white/10 focus:border-primary text-white"
|
||||||
|
placeholder="••••••••"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="confirmPassword">Passwort bestätigen</Label>
|
||||||
|
<Input
|
||||||
|
id="confirmPassword"
|
||||||
|
type="password"
|
||||||
|
value={confirmPassword}
|
||||||
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
|
className="bg-slate-950 border-white/10 focus:border-primary text-white"
|
||||||
|
placeholder="••••••••"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{passwordMessage && (
|
||||||
|
<div
|
||||||
|
className={`p-3 rounded-lg text-sm border ${
|
||||||
|
passwordMessage.type === 'success'
|
||||||
|
? 'bg-green-500/10 border-green-500/20 text-green-400'
|
||||||
|
: 'bg-red-500/10 border-red-500/20 text-red-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{passwordMessage.text}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit" disabled={isUpdatingPassword} className="gap-2">
|
||||||
|
{isUpdatingPassword ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="w-4 h-4 animate-spin" /> Ändern...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<KeyRound className="w-4 h-4" /> Passwort aktualisieren
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
131
shop/app/account-kunden/page.tsx
Normal file
131
shop/app/account-kunden/page.tsx
Normal file
@@ -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 (
|
||||||
|
<div className="min-h-screen bg-[#020617] text-white px-4 py-12">
|
||||||
|
<div className="max-w-4xl mx-auto space-y-8">
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<Link href="/">
|
||||||
|
<Button variant="ghost" size="sm" className="text-slate-400 hover:text-white">
|
||||||
|
<ArrowLeft className="w-4 h-4 mr-2" /> Startseite
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-3xl font-extrabold tracking-tight flex items-center gap-3">
|
||||||
|
<UserIcon className="w-8 h-8 text-primary" />
|
||||||
|
Account Einstellungen
|
||||||
|
</h1>
|
||||||
|
<p className="text-slate-400 text-sm mt-1">
|
||||||
|
Verwalten Sie Ihre persönlichen Profildaten, Unternehmensdaten und Sicherheitseinstellungen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Dashboard Grid */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
|
||||||
|
{/* Linke Infospalte */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
<Card className="glass-dark border-white/10">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-lg flex items-center gap-2">
|
||||||
|
<UserIcon className="w-5 h-5 text-primary" />
|
||||||
|
Benutzer-Info
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="text-slate-400 text-xs">
|
||||||
|
Ihr aktueller Systemstatus
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4 text-sm">
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-500 block text-xs uppercase tracking-wider">E-Mail-Adresse</span>
|
||||||
|
<span className="text-white font-medium">{user.email}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-500 block text-xs uppercase tracking-wider">Rolle</span>
|
||||||
|
<span className="text-amber-400 font-bold uppercase text-xs tracking-wider bg-amber-500/10 px-2 py-0.5 rounded border border-amber-500/20">
|
||||||
|
{dbUser?.role || 'partner'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{company && (
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-500 block text-xs uppercase tracking-wider">Unternehmen</span>
|
||||||
|
<span className="text-white font-medium flex items-center gap-1.5 mt-1">
|
||||||
|
<Building2 className="w-4 h-4 text-slate-400" />
|
||||||
|
{company.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{!dbUser?.company_id && (
|
||||||
|
<Card className="border-red-500/20 bg-red-500/5">
|
||||||
|
<CardContent className="p-4 flex gap-3 text-red-400 text-sm">
|
||||||
|
<ShieldAlert className="w-5 h-5 shrink-0 mt-0.5" />
|
||||||
|
<div>
|
||||||
|
<p className="font-bold">Kein Unternehmen zugewiesen</p>
|
||||||
|
<p className="text-xs text-red-400/80 mt-1">
|
||||||
|
Wenden Sie sich an einen Administrator, um Ihrem Account ein Partner-Unternehmen zuzuweisen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Rechte Formularspalte */}
|
||||||
|
<div className="md:col-span-2">
|
||||||
|
<AccountSettingsForm
|
||||||
|
userId={user.id}
|
||||||
|
profile={profile}
|
||||||
|
company={company}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { createAdminClient } from '@/lib/supabase/admin';
|
import { createAdminClient } from '@/lib/supabase/admin';
|
||||||
|
import { createClient } from '@/lib/supabase/server';
|
||||||
import { revalidatePath, unstable_noStore as noStore } from 'next/cache';
|
import { revalidatePath, unstable_noStore as noStore } from 'next/cache';
|
||||||
import { sendMail } from '@/utils/mail';
|
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) {
|
function getBeautifulEmailHtml(title: string, messageHtml: string, buttonText?: string, buttonUrl?: string) {
|
||||||
return `
|
return `
|
||||||
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; background-color: #f8fafc; padding: 40px 10px; margin: 0; width: 100%;">
|
<div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; background-color: #f8fafc; padding: 40px 10px; margin: 0; width: 100%;">
|
||||||
|
|||||||
Reference in New Issue
Block a user