Files
webshop/shop/app/admin/settings/page.tsx

283 lines
11 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { motion } from 'framer-motion';
import { createClient } from '@/lib/supabase/client';
import { Loader2, Mail, Server, ShieldCheck, Key, Send, CheckCircle2, AlertCircle } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
interface Settings {
host: string;
port: number;
secure: boolean;
user: string;
pass: string;
}
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { staggerChildren: 0.1 }
}
};
const itemVariants = {
hidden: { opacity: 0, y: 15 },
visible: { opacity: 1, y: 0, transition: { type: 'spring' as const, stiffness: 80, damping: 15 } }
};
export default function SettingsPage() {
const [settings, setSettings] = useState<Settings | null>(null);
const [loading, setLoading] = useState(true);
const [message, setMessage] = useState('');
const [msgType, setMsgType] = useState<'success' | 'error' | ''>('');
const [saving, setSaving] = useState(false);
const [testing, setTesting] = useState(false);
const router = useRouter();
useEffect(() => {
async function checkAccessAndLoad() {
try {
const supabase = createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
router.push('/auth/login');
return;
}
const [userRes, smtpRes] = await Promise.all([
supabase.from('users').select('role').eq('id', user.id).single(),
fetch('/api/admin/smtp-settings').then(res => res.json())
]);
if (!userRes.data || userRes.data.role === 'verwaltung') {
router.push('/admin');
return;
}
if (smtpRes.settings) {
setSettings(smtpRes.settings as Settings);
} else {
setMessage('Fehler beim Laden der SMTP-Einstellungen.');
setMsgType('error');
}
} catch (err) {
setMessage('Fehler beim Laden der Einstellungen.');
setMsgType('error');
} finally {
setLoading(false);
}
}
checkAccessAndLoad();
}, [router]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type, checked } = e.target;
setSettings((prev) =>
prev && {
...prev,
[name]: type === 'checkbox' ? checked : name === 'port' ? Number(value) : value,
}
);
};
const handleSave = async (e: React.FormEvent) => {
e.preventDefault();
setSaving(true);
setMessage('');
try {
const res = await fetch('/api/admin/smtp-settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(settings),
});
const data = await res.json();
if (res.ok) {
setMessage('SMTP-Einstellungen erfolgreich gespeichert.');
setMsgType('success');
} else {
setMessage(data.error || 'Fehler beim Speichern.');
setMsgType('error');
}
} catch {
setMessage('Verbindungsfehler beim Speichern.');
setMsgType('error');
} finally {
setSaving(false);
}
};
const handleTestEmail = async () => {
setTesting(true);
setMessage('');
try {
const testPayload = {
to: settings?.user || '',
subject: 'Test-E-Mail aus dem CASPOS Webshop Admin',
text: 'Dies ist eine automatische Test-E-Mail zur Bestätigung der SMTP-Konfiguration.',
};
const res = await fetch('/api/admin/send-test-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(testPayload),
});
const data = await res.json();
if (res.ok) {
setMessage('Test-E-Mail erfolgreich versendet.');
setMsgType('success');
} else {
setMessage(data.error || 'Versand der Test-E-Mail fehlgeschlagen.');
setMsgType('error');
}
} catch {
setMessage('Fehler beim Senden der Test-E-Mail.');
setMsgType('error');
} finally {
setTesting(false);
}
};
if (loading) {
return <div className="p-8 text-white flex justify-center items-center"><Loader2 className="w-8 h-8 animate-spin text-primary" /></div>;
}
return (
<div className="p-6 max-w-5xl mx-auto text-slate-900 dark:text-white space-y-6">
{/* Header */}
<motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} className="space-y-1">
<h1 className="text-3xl font-extrabold tracking-tight flex items-center gap-3">
<Mail className="w-8 h-8 text-sky-400" />
SMTP-Einstellungen
</h1>
<p className="text-slate-400 text-xs">
Konfigurieren Sie den E-Mail-Server für transaktionale Benachrichtigungen und Bestellbestätigungen.
</p>
</motion.div>
{/* Dynamic Status Message */}
{message && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
className={`p-3.5 rounded-xl text-xs font-semibold border flex items-center gap-2 ${
msgType === 'success'
? 'bg-emerald-500/10 border-emerald-500/20 text-emerald-400'
: 'bg-destructive/10 border-destructive/20 text-destructive'
}`}
>
{msgType === 'success' ? <CheckCircle2 className="w-4 h-4 shrink-0" /> : <AlertCircle className="w-4 h-4 shrink-0" />}
{message}
</motion.div>
)}
{/* Bento Grid layout for SMTP */}
<form onSubmit={handleSave}>
<motion.div variants={containerVariants} initial="hidden" animate="visible" className="grid grid-cols-1 md:grid-cols-3 gap-5">
{/* Bento Box 1: Server Connection Details (2 Spalten) */}
<motion.div variants={itemVariants} className="md:col-span-2 p-6 rounded-2xl bg-slate-900/60 border border-slate-800 backdrop-blur-md space-y-4 shadow-md">
<div className="flex items-center gap-2 text-sky-400 font-bold text-sm border-b border-slate-800 pb-3">
<Server className="w-4 h-4" />
<span>Server & Serververbindung</span>
</div>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div className="sm:col-span-2 space-y-1.5">
<Label htmlFor="host" className="text-xs font-bold text-slate-300">SMTP Host</Label>
<Input
id="host"
name="host"
type="text"
required
value={settings?.host ?? ''}
onChange={handleChange}
placeholder="smtp.beispiel.de"
className="bg-slate-950/80 border-slate-800 text-white rounded-xl font-mono text-sm"
/>
</div>
<div className="space-y-1.5">
<Label htmlFor="port" className="text-xs font-bold text-slate-300">Port</Label>
<Input
id="port"
name="port"
type="number"
required
value={settings?.port ?? ''}
onChange={handleChange}
placeholder="587"
className="bg-slate-950/80 border-slate-800 text-white rounded-xl font-mono text-sm"
/>
</div>
</div>
<div className="pt-2">
<label className="inline-flex items-center gap-2 cursor-pointer p-3 rounded-xl bg-slate-950/50 border border-slate-800/80 hover:bg-slate-800/40 transition">
<input
name="secure"
type="checkbox"
checked={settings?.secure ?? false}
onChange={handleChange}
className="w-4 h-4 rounded text-sky-500 bg-slate-900 border-slate-700"
/>
<span className="text-xs font-semibold text-slate-200">SSL / TLS Verschlüsselung aktivieren</span>
</label>
</div>
</motion.div>
{/* Bento Box 2: Auth Credentials (1 Spalte) */}
<motion.div variants={itemVariants} className="p-6 rounded-2xl bg-slate-900/60 border border-slate-800 backdrop-blur-md space-y-4 shadow-md flex flex-col justify-between">
<div className="space-y-4">
<div className="flex items-center gap-2 text-sky-400 font-bold text-sm border-b border-slate-800 pb-3">
<Key className="w-4 h-4" />
<span>Zugangsdaten</span>
</div>
<div className="space-y-1.5">
<Label htmlFor="user" className="text-xs font-bold text-slate-300">SMTP Benutzer</Label>
<Input
id="user"
name="user"
type="email"
required
value={settings?.user ?? ''}
onChange={handleChange}
placeholder="absender@beispiel.de"
className="bg-slate-950/80 border-slate-800 text-white rounded-xl font-mono text-sm"
/>
</div>
<div className="space-y-1.5">
<Label htmlFor="pass" className="text-xs font-bold text-slate-300">Passwort</Label>
<Input
id="pass"
name="pass"
type="password"
required
value={settings?.pass ?? ''}
onChange={handleChange}
placeholder="••••••••••••"
className="bg-slate-950/80 border-slate-800 text-white rounded-xl font-mono text-sm"
/>
</div>
</div>
{/* Submit & Test Buttons */}
<div className="flex flex-col gap-2 pt-4 border-t border-slate-800">
<Button type="submit" disabled={saving || testing} className="w-full bg-sky-600 hover:bg-sky-500 text-white rounded-xl h-10 text-xs font-bold">
{saving ? <><Loader2 className="w-4 h-4 mr-2 animate-spin" /> Speichert...</> : 'Konfiguration speichern'}
</Button>
<Button type="button" onClick={handleTestEmail} disabled={saving || testing} variant="outline" className="w-full border-slate-700 text-slate-300 hover:text-white rounded-xl h-10 text-xs font-bold">
{testing ? <><Loader2 className="w-4 h-4 mr-2 animate-spin" /> Sendet...</> : <><Send className="w-3.5 h-3.5 mr-2 text-emerald-400" /> Test-Mail senden</>}
</Button>
</div>
</motion.div>
</motion.div>
</form>
</div>
);
}