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

@@ -1,9 +1,10 @@
'use client';
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { motion, AnimatePresence } from 'framer-motion';
import { Scan, GlassWater, Users, Settings, ArrowRight, X, Sparkles } from 'lucide-react';
import { useI18n } from '@/i18n/I18nContext';
const ONBOARDING_KEY = 'dramlog_onboarding_complete';
@@ -14,40 +15,42 @@ interface OnboardingStep {
description: string;
}
const STEPS: OnboardingStep[] = [
const getSteps = (t: (path: string) => string): OnboardingStep[] => [
{
id: 'welcome',
icon: <Sparkles size={32} className="text-orange-500" />,
title: 'Willkommen bei DramLog!',
description: 'Dein persönliches Whisky-Tagebuch. Scanne, bewerte und entdecke neue Drams.',
title: t('tutorial.steps.welcome.title'),
description: t('tutorial.steps.welcome.desc'),
},
{
id: 'scan',
icon: <Scan size={32} className="text-orange-500" />,
title: 'Scanne deine Flaschen',
description: 'Fotografiere das Etikett einer Flasche die KI erkennt automatisch alle Details.',
title: t('tutorial.steps.scan.title'),
description: t('tutorial.steps.scan.desc'),
},
{
id: 'taste',
icon: <GlassWater size={32} className="text-orange-500" />,
title: 'Bewerte deine Drams',
description: 'Füge Tasting-Notizen hinzu und behalte den Überblick über deine Lieblings-Whiskys.',
title: t('tutorial.steps.taste.title'),
description: t('tutorial.steps.taste.desc'),
},
{
id: 'session',
id: 'activity',
icon: <Users size={32} className="text-orange-500" />,
title: 'Tasting-Sessions',
description: 'Organisiere Verkostungen mit Freunden und vergleicht eure Bewertungen.',
title: t('tutorial.steps.activity.title'),
description: t('tutorial.steps.activity.desc'),
},
{
id: 'ready',
icon: <Settings size={32} className="text-orange-500" />,
title: 'Bereit zum Start!',
description: 'Scanne jetzt deine erste Flasche mit dem orangefarbenen Button unten.',
title: t('tutorial.steps.ready.title'),
description: t('tutorial.steps.ready.desc'),
},
];
export default function OnboardingTutorial() {
const { t } = useI18n();
const STEPS = getSteps(t);
const [isOpen, setIsOpen] = useState(false);
const [currentStep, setCurrentStep] = useState(0);
const pathname = usePathname();
@@ -148,14 +151,14 @@ export default function OnboardingTutorial() {
onClick={handleSkip}
className="flex-1 py-3 px-4 text-sm font-bold text-zinc-500 hover:text-white transition-colors"
>
Überspringen
{t('tutorial.skip')}
</button>
)}
<button
onClick={handleNext}
className="flex-1 py-3 px-4 bg-orange-600 hover:bg-orange-500 text-white font-bold text-sm rounded-xl flex items-center justify-center gap-2 transition-colors"
>
{isLastStep ? 'Los geht\'s!' : 'Weiter'}
{isLastStep ? t('tutorial.finish') : t('tutorial.next')}
<ArrowRight size={16} />
</button>
</div>