feat: public split visibility, RLS recursion fixes, and consolidated tasting permission management

- Added public discovery section for active splits on the landing page
- Refactored split detail page for guest support and login redirects
- Extracted SplitCard component for reuse
- Consolidated RLS policies for bottles and tastings to resolve permission errors
- Added unified SQL consolidation script for RLS and naming fixes
- Enhanced service logging for better database error diagnostics
This commit is contained in:
2025-12-28 22:02:46 +01:00
parent 332bfdaf02
commit 9d6a8b358f
25 changed files with 2014 additions and 495 deletions

View File

@@ -5,7 +5,10 @@ import { motion } from 'framer-motion';
import { Lock, Eye, EyeOff, Loader2, CheckCircle, AlertCircle } from 'lucide-react';
import { changePassword } from '@/services/profile-actions';
import { useI18n } from '@/i18n/I18nContext';
export default function PasswordChangeForm() {
const { t } = useI18n();
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
@@ -20,13 +23,13 @@ export default function PasswordChangeForm() {
if (newPassword !== confirmPassword) {
setStatus('error');
setError('Passwörter stimmen nicht überein');
setError(t('settings.password.mismatch'));
return;
}
if (newPassword.length < 6) {
setStatus('error');
setError('Passwort muss mindestens 6 Zeichen lang sein');
setError(t('settings.password.tooShort'));
return;
}
@@ -43,7 +46,7 @@ export default function PasswordChangeForm() {
setTimeout(() => setStatus('idle'), 3000);
} else {
setStatus('error');
setError(result.error || 'Fehler beim Ändern');
setError(result.error || t('common.error'));
}
});
};
@@ -58,14 +61,14 @@ export default function PasswordChangeForm() {
>
<h2 className="text-lg font-bold text-white mb-6 flex items-center gap-2">
<Lock size={20} className="text-orange-500" />
Passwort ändern
{t('settings.password.title')}
</h2>
<div className="space-y-4">
{/* New Password */}
<div>
<label className="block text-sm font-medium text-zinc-400 mb-2">
Neues Passwort
{t('settings.password.newPassword')}
</label>
<div className="relative">
<input
@@ -88,7 +91,7 @@ export default function PasswordChangeForm() {
{/* Confirm Password */}
<div>
<label className="block text-sm font-medium text-zinc-400 mb-2">
Passwort bestätigen
{t('settings.password.confirmPassword')}
</label>
<input
type={showPassword ? 'text' : 'password'}
@@ -107,12 +110,12 @@ export default function PasswordChangeForm() {
{newPassword === confirmPassword ? (
<>
<CheckCircle size={12} />
Passwörter stimmen überein
{t('settings.password.match')}
</>
) : (
<>
<AlertCircle size={12} />
Passwörter stimmen nicht überein
{t('settings.password.mismatch')}
</>
)}
</div>
@@ -122,7 +125,7 @@ export default function PasswordChangeForm() {
{status === 'success' && (
<div className="mt-4 p-3 bg-green-500/10 border border-green-500/20 rounded-xl flex items-center gap-2 text-green-500 text-sm">
<CheckCircle size={16} />
Passwort erfolgreich geändert!
{t('settings.password.success')}
</div>
)}
{status === 'error' && (
@@ -141,12 +144,12 @@ export default function PasswordChangeForm() {
{isPending ? (
<>
<Loader2 size={18} className="animate-spin" />
Ändern...
{t('common.loading')}
</>
) : (
<>
<Lock size={18} />
Passwort ändern
{t('settings.password.change')}
</>
)}
</button>