From fe279f649cdcd5864f65bfee4c18cf8dd4a9bdae Mon Sep 17 00:00:00 2001 From: averel10 Date: Fri, 10 Apr 2026 08:29:45 +0200 Subject: [PATCH] feat: reworked registration/sign-in, auth is now on homescreen and includes an explanation text. Reworked onboarding to only include a participation checkbox for the raffle. --- .../datasets/[id]/entries/[entryId]/page.tsx | 2 +- src/app/admin/datasets/[id]/page.tsx | 2 +- src/app/admin/datasets/page.tsx | 2 +- src/app/admin/experiments/[id]/page.tsx | 2 +- .../[id]/participants/[userId]/page.tsx | 2 +- .../experiments/[id]/participants/page.tsx | 2 +- src/app/admin/experiments/page.tsx | 2 +- src/app/admin/page.tsx | 2 +- src/app/admin/users/page.tsx | 2 +- src/app/annotate/[experimentId]/page.tsx | 2 +- src/app/page.tsx | 17 +-- src/app/user/sign-in/page.tsx | 5 - src/components/AuthForm.tsx | 11 +- src/components/Header.tsx | 103 ++++++++---------- .../OnboardingViews/OnboardingFormView.tsx | 38 ++++--- .../OnboardingViews/OnboardingPhase.tsx | 11 +- src/lib/auth.ts | 12 ++ 17 files changed, 109 insertions(+), 108 deletions(-) delete mode 100644 src/app/user/sign-in/page.tsx diff --git a/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx b/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx index 74cf58a..1652a30 100644 --- a/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx +++ b/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx @@ -17,7 +17,7 @@ export default async function DatasetEntryPage({ params }: DatasetEntryPageProps const result = await requireAdmin(); if (!result.authenticated) { - redirect("/user/sign-in"); + redirect("/"); } if (!result.admin) { diff --git a/src/app/admin/datasets/[id]/page.tsx b/src/app/admin/datasets/[id]/page.tsx index ebd184b..3080fb0 100644 --- a/src/app/admin/datasets/[id]/page.tsx +++ b/src/app/admin/datasets/[id]/page.tsx @@ -22,7 +22,7 @@ export default async function DatasetPage({ params }: DatasetPageProps) { const result = await requireAdmin(); if (!result.authenticated) { - redirect("/user/sign-in"); + redirect("/"); } if (!result.admin) { diff --git a/src/app/admin/datasets/page.tsx b/src/app/admin/datasets/page.tsx index a5343f5..e2660e9 100644 --- a/src/app/admin/datasets/page.tsx +++ b/src/app/admin/datasets/page.tsx @@ -8,7 +8,7 @@ export default async function DatasetsAdminPage() { const result = await requireAdmin(); if (!result.authenticated) { - redirect("/user/sign-in"); + redirect("/"); } if (!result.admin) { diff --git a/src/app/admin/experiments/[id]/page.tsx b/src/app/admin/experiments/[id]/page.tsx index cf3ac46..02a8531 100644 --- a/src/app/admin/experiments/[id]/page.tsx +++ b/src/app/admin/experiments/[id]/page.tsx @@ -22,7 +22,7 @@ export default async function ExperimentPage({ params }: ExperimentPageProps) { const result = await requireAdmin(); if (!result.authenticated) { - redirect("/user/sign-in"); + redirect("/"); } if (!result.admin) { diff --git a/src/app/admin/experiments/[id]/participants/[userId]/page.tsx b/src/app/admin/experiments/[id]/participants/[userId]/page.tsx index 612bf5b..960a153 100644 --- a/src/app/admin/experiments/[id]/participants/[userId]/page.tsx +++ b/src/app/admin/experiments/[id]/participants/[userId]/page.tsx @@ -18,7 +18,7 @@ export default async function ParticipantPage({ params }: ParticipantDetailPageP const result = await requireAdmin(); if (!result.authenticated) { - redirect("/user/sign-in"); + redirect("/"); } if (!result.admin) { diff --git a/src/app/admin/experiments/[id]/participants/page.tsx b/src/app/admin/experiments/[id]/participants/page.tsx index ab603f7..951128c 100644 --- a/src/app/admin/experiments/[id]/participants/page.tsx +++ b/src/app/admin/experiments/[id]/participants/page.tsx @@ -17,7 +17,7 @@ export default async function ParticipantsPage({ params }: ParticipantsPageProps const result = await requireAdmin(); if (!result.authenticated) { - redirect("/user/sign-in"); + redirect("/"); } if (!result.admin) { diff --git a/src/app/admin/experiments/page.tsx b/src/app/admin/experiments/page.tsx index e737845..af465bb 100644 --- a/src/app/admin/experiments/page.tsx +++ b/src/app/admin/experiments/page.tsx @@ -8,7 +8,7 @@ export default async function ExperimentsAdminPage() { const result = await requireAdmin(); if (!result.authenticated) { - redirect("/user/sign-in"); + redirect("/"); } if (!result.admin) { diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index aa1145f..83087c3 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -10,7 +10,7 @@ export default async function AdminPage() { const result = await requireAdmin(); if (!result.authenticated) { - redirect("/user/sign-in"); + redirect("/"); } if (!result.admin) { diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index 1858289..b014158 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -8,7 +8,7 @@ export default async function UsersPage() { const result = await requireAdmin(); if (!result.authenticated) { - redirect("/user/sign-in"); + redirect("/"); } if (!result.admin) { diff --git a/src/app/annotate/[experimentId]/page.tsx b/src/app/annotate/[experimentId]/page.tsx index a6d1713..3e032c7 100644 --- a/src/app/annotate/[experimentId]/page.tsx +++ b/src/app/annotate/[experimentId]/page.tsx @@ -16,7 +16,7 @@ interface Props { export default async function AnnotatePage({ params }: Props) { const session = await auth.api.getSession({ headers: await headers() }); - if (!session) redirect('/user/sign-in'); + if (!session) redirect("/"); const { experimentId: experimentIdStr } = await params; const experimentId = parseInt(experimentIdStr, 10); diff --git a/src/app/page.tsx b/src/app/page.tsx index b50ecb2..3effb57 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,25 +2,18 @@ import Link from 'next/link'; import { headers } from 'next/headers'; import { auth } from '@/lib/auth'; import { getAllExperiments, getAnnotationProgress } from '@/app/actions/annotations'; +import { AuthForm } from '@/components/AuthForm'; export default async function HomePage() { const session = await auth.api.getSession({ headers: await headers() }); if (!session) { return ( -
-

- Willkommen zur Dialektannotation -

-

- Bitte melden Sie sich an, um mit der Annotation zu beginnen. +

+

+ Für die Teilnahme an Annotationsstudien benötigst du ein Benutzerkonto. Mit diesem Konto speichern wir deinen Fortschritt, damit du jederzeit unterbrechen und später fortsetzen kannst. Deine Daten werden vertraulich behandelt und ausschließlich für die Zwecke dieser Studien verwendet. Wir werden dir niemals E-Mails ohne deine ausdrückliche Zustimmung senden.

- - Anmelden - +
); } diff --git a/src/app/user/sign-in/page.tsx b/src/app/user/sign-in/page.tsx deleted file mode 100644 index c5daa5d..0000000 --- a/src/app/user/sign-in/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { AuthForm } from '@/components/AuthForm'; - -export default function SignInPage() { - return ; -} diff --git a/src/components/AuthForm.tsx b/src/components/AuthForm.tsx index f8d4e2d..d082f73 100644 --- a/src/components/AuthForm.tsx +++ b/src/components/AuthForm.tsx @@ -57,13 +57,10 @@ export function AuthForm() { }; return ( -
-
+
+
{/* Card */}
- {/* Header */} -
-
{/* Tabs */}
@@ -200,7 +197,7 @@ export function AuthForm() {

{mode === 'signin' ? ( <> - Sie haben noch kein Konto?{' '} + Du hast noch kein Konto?{' '} -

- ) : ( - - Anmelden - - )} - + Abmelden + +
+ )} + + )} {/* Mobile Menu Button */} - + {(loading || session) && ( + + )}
{/* Mobile Fullscreen Menu */} - {mobileMenuOpen && ( + {mobileMenuOpen && session && (
{loading ? ( Laden... - ) : session ? ( + ) : ( <>

Willkommen zurück

@@ -111,14 +108,6 @@ export function Header() { Abmelden - ) : ( - setMobileMenuOpen(false)} - className="px-4 py-3 bg-green-500 hover:bg-green-600 text-white font-semibold rounded-lg transition-colors text-center" - > - Anmelden - )}
)} diff --git a/src/components/OnboardingViews/OnboardingFormView.tsx b/src/components/OnboardingViews/OnboardingFormView.tsx index 95b21a9..c114f08 100644 --- a/src/components/OnboardingViews/OnboardingFormView.tsx +++ b/src/components/OnboardingViews/OnboardingFormView.tsx @@ -8,6 +8,7 @@ import { DIALECT_LABELS_WITHOUT_DE } from '@/lib/dialects'; interface OnboardingFormViewProps { experimentId: number; onBack: () => void; + userEmail: string; } interface ResidenceEntry { @@ -17,7 +18,7 @@ interface ResidenceEntry { interface FormData { age?: string; - email?: string; + participateInRaffle?: boolean; residences?: ResidenceEntry[]; dialectQualities?: Record; listeningExperience?: string; @@ -26,7 +27,7 @@ interface FormData { type Step = 1 | 2 | 3; -export default function OnboardingFormView({ experimentId, onBack }: OnboardingFormViewProps) { +export default function OnboardingFormView({ experimentId, onBack, userEmail }: OnboardingFormViewProps) { const router = useRouter(); const [isPending, startTransition] = useTransition(); const [currentStep, setCurrentStep] = useState(1); @@ -37,7 +38,7 @@ export default function OnboardingFormView({ experimentId, onBack }: OnboardingF const [formData, setFormData] = useState({ age: '', - email: '', + participateInRaffle: false, residences: [{ region: '', years: '' }], dialectQualities: initializeDialectQualities() }); @@ -50,7 +51,7 @@ export default function OnboardingFormView({ experimentId, onBack }: OnboardingF }); }, [currentStep]); - const handleChange = (field: keyof FormData, value: string) => { + const handleChange = (field: keyof FormData, value: string | boolean) => { setFormData((prev) => ({ ...prev, [field]: value, @@ -223,18 +224,23 @@ export default function OnboardingFormView({ experimentId, onBack }: OnboardingF
- -

Wird nur für die Gutschein-Verlosung verwendet.

- handleChange('email', e.target.value)} - placeholder="beispiel@email.ch" - className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> +
+ handleChange('participateInRaffle', e.target.checked ? true : false)} + className="mt-1 w-5 h-5 cursor-pointer accent-blue-600" + /> +
+ +

+ Wir werden deine E-Mail-Adresse{userEmail && ` (${userEmail})`} verwenden, um dich im Falle eines Gewinns zu kontaktieren. +

+
+
)} diff --git a/src/components/OnboardingViews/OnboardingPhase.tsx b/src/components/OnboardingViews/OnboardingPhase.tsx index 06be2e1..d03d47c 100644 --- a/src/components/OnboardingViews/OnboardingPhase.tsx +++ b/src/components/OnboardingViews/OnboardingPhase.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'; import { getOnboardingAnswers } from '@/app/actions/onboarding'; import OnboardingInfoView from './OnboardingInfoView'; import OnboardingFormView from './OnboardingFormView'; +import { authClient } from '@/lib/auth-client'; const scrollToTop = () => { window.scrollTo({ @@ -21,6 +22,9 @@ export default function OnboardingPhase({ experimentId }: OnboardingPhaseProps) const [hasExistingAnswers, setHasExistingAnswers] = useState(false); const [isLoading, setIsLoading] = useState(true); + const [userEmail, setUserEmail] = useState(''); + + useEffect(() => { const load = async () => { try { @@ -32,6 +36,11 @@ export default function OnboardingPhase({ experimentId }: OnboardingPhaseProps) setHasExistingAnswers(true); } } + // Get user email for raffle participation + const session = await authClient.getSession(); + if (session && session.data && session.data.user && session.data.user.email) { + setUserEmail(session.data.user.email); + } } catch (err) { console.error(err); } finally { @@ -69,5 +78,5 @@ export default function OnboardingPhase({ experimentId }: OnboardingPhaseProps) } - return { scrollToTop(); setShowInfoPage(true); }} />; + return { scrollToTop(); setShowInfoPage(true); }} />; } diff --git a/src/lib/auth.ts b/src/lib/auth.ts index acd56c4..f59ec1f 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -41,4 +41,16 @@ export async function requireAdmin() { } return { authenticated: true, admin: session.user.admin, session }; +} + +export async function requireAuthentication() { + const session = await auth.api.getSession({ + headers: await headers() + }); + + if (!session) { + return { authenticated: false, session: null }; + } + + return { authenticated: true, session }; } \ No newline at end of file