Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
296 changes: 236 additions & 60 deletions src/app/globals.css

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions src/app/jobs-polish.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.jobs-filter-trigger {
min-height: 2.8rem;
border-radius: 999px !important;
padding-inline: 1rem 1.12rem !important;
box-shadow: var(--shadow-card) !important;
}

.jobs-filter-trigger[data-active="true"] {
border-color: var(--brand-border) !important;
background: var(--surface-solid) !important;
color: var(--text-strong) !important;
}

.dark .jobs-filter-trigger {
border-color: rgba(148, 163, 184, 0.12) !important;
background: rgba(8, 13, 29, 0.72) !important;
box-shadow: 0 18px 44px rgba(0, 0, 0, 0.24), inset 0 1px 0 rgba(255, 255, 255, 0.045) !important;
}

.dark .jobs-filter-trigger:hover,
.dark .jobs-filter-trigger[data-active="true"] {
border-color: rgba(129, 140, 248, 0.28) !important;
background: rgba(255, 255, 255, 0.1) !important;
color: #ffffff !important;
}

.jobs-empty-state {
position: relative;
isolation: isolate;
border-color: transparent !important;
background:
linear-gradient(180deg, color-mix(in srgb, var(--surface-raised) 88%, transparent), color-mix(in srgb, var(--surface-solid) 84%, transparent)) !important;
box-shadow:
0 0 0 1px color-mix(in srgb, var(--border-subtle) 72%, transparent),
0 22px 58px rgba(31, 45, 74, 0.09),
inset 0 1px 0 rgba(255, 255, 255, 0.34) !important;
}

.dark .jobs-empty-state {
background:
linear-gradient(180deg, rgba(12, 19, 36, 0.92), rgba(7, 12, 27, 0.96)) !important;
box-shadow:
0 0 0 1px rgba(148, 163, 184, 0.085),
0 24px 70px rgba(0, 0, 0, 0.28) !important;
}

.jobs-empty-title {
text-wrap: balance;
}

.jobs-empty-message {
text-wrap: pretty;
}

.jobs-empty-icon {
border-color: color-mix(in srgb, var(--border-subtle) 82%, transparent) !important;
background: color-mix(in srgb, var(--surface-muted) 76%, transparent) !important;
color: color-mix(in srgb, var(--brand) 56%, var(--text-muted)) !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.12),
0 10px 26px rgba(31, 45, 74, 0.08) !important;
}

.dark .jobs-empty-icon {
border-color: rgba(148, 163, 184, 0.09) !important;
background: rgba(15, 23, 42, 0.48) !important;
color: rgba(129, 140, 248, 0.54) !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.035),
0 12px 30px rgba(0, 0, 0, 0.18) !important;
}
1 change: 1 addition & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TestModeBanner } from "@/components/admin/TestModeBanner";
import { getCurrentSessionAndProfile } from "@/lib/auth";
import { normalizeThemePreference, type ThemePreference } from "@/lib/theme-preference";
import "./globals.css";
import "./jobs-polish.css";

const fontSans = Plus_Jakarta_Sans({
variable: "--font-sans",
Expand Down
12 changes: 5 additions & 7 deletions src/app/onboarding/waitlist/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ type WaitlistShellProps = {
size?: string;
};

function WaitlistShell({ children, size = "max-w-2xl" }: WaitlistShellProps) {
function WaitlistShell({ children, size = "max-w-3xl" }: WaitlistShellProps) {
return (
<div className="landing-auth-shell min-h-dvh bg-[#07090f]">
<div className="landing-auth-shell min-h-dvh bg-background">
<main className="onboarding-shell waitlist-shell flex min-h-dvh items-start justify-center overflow-y-auto overflow-x-hidden px-4 py-4 pb-16 no-scrollbar md:items-center md:py-4 md:pb-16">
<div className={`onboarding-panel waitlist-panel relative w-full ${size} overflow-hidden rounded-3xl p-6 md:p-12`}>
<div className={`onboarding-panel waitlist-panel relative w-full ${size} overflow-hidden rounded-[2rem] p-6 md:p-12`}>
<div className="onboarding-panel-glow pointer-events-none absolute -left-20 -top-20 h-56 w-56" />
<div className="onboarding-panel-texture pointer-events-none absolute inset-0" />
<div className="relative z-10">
Expand Down Expand Up @@ -92,7 +92,7 @@ function WaitlistContent() {

if (success) {
return (
<WaitlistShell size="max-w-md">
<WaitlistShell size="max-w-lg">
<div className="waitlist-success text-center">
<div className="waitlist-success-icon mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full">
<CheckCircle2 className="h-8 w-8" aria-hidden="true" />
Expand All @@ -118,9 +118,7 @@ function WaitlistContent() {
<LogoBadge size="md" className="waitlist-header-badge shrink-0" />
<div className="min-w-0 flex-1">
<h1 className="waitlist-heading text-2xl font-bold leading-tight tracking-tight text-white md:text-3xl">
<span className="block">{cityLabel} ist noch</span>
{" "}
<span className="block">nicht gestartet.</span>
JobBridge in {cityLabel} ist noch nicht einsatzbereit.
</h1>
<p className="waitlist-copy mt-2.5 leading-relaxed text-slate-300">
Trag dich ein, und wir informieren dich, sobald JobBridge in <strong>{cityLabel}</strong> verfügbar ist.
Expand Down
2 changes: 1 addition & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default async function LandingPage({

// Ohne Session zeigt der Client-Wizard ggf. einen lokal gespeicherten Pending-Onboarding-Stand.
return (
<div className="landing-auth-shell min-h-dvh bg-[#07090f]">
<div className="landing-auth-shell min-h-dvh bg-background">
<AuthBridge authState={authState} redirectTo={redirectTo} initialMode={authMode} />
<MiniFooter />
</div>
Expand Down
46 changes: 28 additions & 18 deletions src/components/OnboardingWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { supabaseBrowser } from "@/lib/supabaseClient";
import { useEmailResend } from "@/lib/hooks/useEmailResend";
import { type OnboardingRole, type Profile } from "@/lib/types";
import { BRAND_EMAIL } from "@/lib/constants";
import { Sparkles, HandHeart, Building2, Mail, UserX, KeyRound } from "lucide-react";
import { Sparkles, HandHeart, Building2, Mail, UserX, KeyRound, UserPlus, LogIn } from "lucide-react";
import { LocationStep } from "./onboarding/LocationStep";
import { CinematicDateInput } from "@/components/ui/CinematicDateInput";
import type { User } from "@supabase/supabase-js";
Expand Down Expand Up @@ -693,7 +693,7 @@ export function OnboardingWizard({
setErrorMsg(null);
};

const panelClass = "onboarding-panel relative rounded-3xl p-8 md:p-12 overflow-hidden";
const panelClass = "onboarding-panel relative rounded-[2rem] p-7 md:p-12 overflow-hidden";
const panelGlowClass = "onboarding-panel-glow pointer-events-none absolute -top-16 -left-16 w-56 h-56";
const panelTextureClass = "onboarding-panel-texture pointer-events-none absolute inset-0";
const emailConfirmStatus = codeMessage || resendError || resendMessage;
Expand Down Expand Up @@ -722,7 +722,7 @@ export function OnboardingWizard({
>
<div
className={[
"relative z-10 my-auto w-full max-w-2xl",
"relative z-10 my-auto w-full max-w-3xl",
].join(" ")}
>
<AnimatePresence initial={false} mode="wait">
Expand All @@ -742,14 +742,14 @@ export function OnboardingWizard({
<div className="flex flex-col items-start gap-4 text-left">
<CardHeader
title="JobBridge"
subtitle="Sichere Taschengeldjobs zwischen Jugendlichen und Auftraggebern."
subtitle="Sichere Taschengeldjobs bieten oder finden."
spacing="tight"
/>
<p className="text-base text-slate-200/80 max-w-md">
Plattform mit verifizierten Aufgaben, klaren Schritten und seniorenfreundlicher Bedienung.
</p>
<div className="pt-2 w-full">
<ButtonPrimary onClick={nextStep} className="w-full">Jetzt starten</ButtonPrimary>
<ButtonPrimary onClick={nextStep} className="onboarding-start-button w-full">Jetzt starten</ButtonPrimary>
</div>
</div>
</div>
Expand All @@ -770,23 +770,28 @@ export function OnboardingWizard({
<div className={panelTextureClass} />

<CardHeader
title="Warst du schon bei JobBridge?"
title="Hast du schon einen JobBridge-Account?"
subtitle="Damit wir dich richtig weiterleiten können."
/>

<div className="grid gap-4 mb-8">
<div className="onboarding-mode-grid grid gap-4 mb-8 md:grid-cols-2">
<ChoiceTile
onClick={() => {
setMode("signup");
setErrorType(null);
setErrorMsg(null);
}}
selected={mode === "signup"}
className="onboarding-choice"
className="onboarding-choice onboarding-mode-choice"
>
<div className="space-y-1">
<div className="onboarding-choice-title text-lg font-semibold">Ich bin neu hier</div>
<div className="onboarding-choice-text text-sm">Ich möchte ein neues Konto erstellen.</div>
<div className="onboarding-choice-content flex h-full items-start gap-4">
<div className="onboarding-choice-icon flex h-11 w-11 shrink-0 items-center justify-center rounded-full">
<UserPlus className="h-5 w-5" aria-hidden="true" />
</div>
<div className="min-w-0 flex-1 space-y-1">
<div className="onboarding-choice-title text-lg font-semibold">Ich bin neu hier</div>
<div className="onboarding-choice-text text-sm">Ich möchte ein neues Konto erstellen.</div>
</div>
</div>
</ChoiceTile>
<ChoiceTile
Expand All @@ -796,11 +801,16 @@ export function OnboardingWizard({
setErrorMsg(null);
}}
selected={mode === "signin"}
className="onboarding-choice"
className="onboarding-choice onboarding-mode-choice"
>
<div className="space-y-1">
<div className="onboarding-choice-title text-lg font-semibold">Ich war schon hier</div>
<div className="onboarding-choice-text text-sm">Ich habe bereits ein Konto.</div>
<div className="onboarding-choice-content flex h-full items-start gap-4">
<div className="onboarding-choice-icon flex h-11 w-11 shrink-0 items-center justify-center rounded-full">
<LogIn className="h-5 w-5" aria-hidden="true" />
</div>
<div className="min-w-0 flex-1 space-y-1">
<div className="onboarding-choice-title text-lg font-semibold">Ich war schon hier</div>
<div className="onboarding-choice-text text-sm">Ich habe bereits ein Konto.</div>
</div>
</div>
</ChoiceTile>
</div>
Expand All @@ -826,7 +836,7 @@ export function OnboardingWizard({
exit={{ opacity: 0, y: -8 }}
transition={{ duration: 0.4, ease: "easeOut" }}
>
<div className={`${panelClass} onboarding-location-panel mx-auto w-full max-w-2xl`}>
<div className={`${panelClass} onboarding-location-panel mx-auto w-full max-w-3xl`}>
<div className={panelGlowClass} />
<div className={panelTextureClass} />
<LocationStep
Expand Down Expand Up @@ -956,7 +966,7 @@ export function OnboardingWizard({
</ButtonPrimary>
<a
href={`mailto:support@jobbridge.app?subject=Hilfe bei Passwort (JobBridge)&body=Hallo Support-Team,%0D%0A%0D%0Amein Passwort für ${email} wird nicht akzeptiert.%0D%0A%0D%0ABitte helft mir weiter.`}
className="onboarding-feedback-action onboarding-feedback-action-secondary flex h-12 w-full items-center justify-center text-sm font-semibold transition-[background-color,color,scale,border-color,box-shadow] duration-200 ease-out active:scale-[0.98]"
className="onboarding-feedback-action onboarding-feedback-action-secondary flex h-12 w-full items-center justify-center text-sm font-semibold transition-[background-color,color,scale,border-color,box-shadow] duration-200 ease-out active:scale-[0.96]"
>
Support kontaktieren
</a>
Expand Down Expand Up @@ -993,7 +1003,7 @@ export function OnboardingWizard({
</ButtonPrimary>
<a
href={`mailto:support@jobbridge.app?subject=Account nicht gefunden (JobBridge)&body=Hallo Support-Team,%0D%0A%0D%0Aich versuche mich mit ${email} anzumelden, aber der Account existiert angeblich nicht.%0D%0A%0D%0ABitte helft mir weiter.`}
className="onboarding-feedback-action onboarding-feedback-action-secondary flex h-12 w-full items-center justify-center text-sm font-semibold transition-[background-color,color,scale,border-color,box-shadow] duration-200 ease-out active:scale-[0.98]"
className="onboarding-feedback-action onboarding-feedback-action-secondary flex h-12 w-full items-center justify-center text-sm font-semibold transition-[background-color,color,scale,border-color,box-shadow] duration-200 ease-out active:scale-[0.96]"
>
Support kontaktieren
</a>
Expand Down
21 changes: 11 additions & 10 deletions src/components/jobs/JobsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export function JobsList({
<>
{/* ── Mobile Tab Bar ───────────────────────────────────────── */}
<div className="mb-7 flex w-full justify-center md:hidden">
<div className="jobs-mobile-tabs flex w-full items-center justify-between rounded-2xl border border-white/[0.08] bg-gradient-to-br from-slate-900/95 via-slate-900/80 to-slate-950/95 p-1.5 shadow-[0_18px_50px_-26px_rgba(0,0,0,0.75)]">
<div className="jobs-mobile-tabs flex w-full items-center justify-between rounded-full border border-white/[0.08] bg-gradient-to-br from-slate-900/95 via-slate-900/80 to-slate-950/95 p-1.5 shadow-[0_18px_50px_-26px_rgba(0,0,0,0.75)]">
<div className="flex items-center gap-0.5 overflow-x-auto no-scrollbar flex-1 min-w-0">
<MobileTab
active={activeTab === "active"}
Expand Down Expand Up @@ -252,7 +252,7 @@ export function JobsList({
onClick={() => setShowFilterPanel(true)}
badgeCount={totalBadgeCount}
isActive={hasChanges}
className="h-9 w-9 rounded-xl"
className="h-10 w-10 rounded-full"
/>
</div>
</div>
Expand Down Expand Up @@ -291,8 +291,9 @@ export function JobsList({

<button
onClick={() => setShowFilterPanel(true)}
data-active={showFilterPanel || hasChanges}
className={cn(
"jobs-filter-trigger relative ml-4 flex items-center gap-2.5 whitespace-nowrap rounded-xl border border-transparent px-3.5 py-2.5 text-slate-400 transition-colors duration-200 hover:border-white/10 hover:bg-white/5 hover:text-white sm:px-4",
"jobs-filter-trigger relative ml-4 flex items-center gap-2.5 whitespace-nowrap rounded-full border border-transparent px-3.5 py-2.5 text-slate-400 transition-[background-color,border-color,color,box-shadow,transform] duration-200 ease-out active:scale-[0.96] sm:px-4",
showFilterPanel && "bg-white/10 text-indigo-300 border-indigo-500/20",
hasChanges && !showFilterPanel && "text-indigo-300 border-indigo-500/20 bg-indigo-500/10"
)}
Expand Down Expand Up @@ -322,7 +323,7 @@ export function JobsList({
emptyMsg={
<EmptyState
icon={Briefcase}
title="Aktuell keine lokalen Jobs"
title="Keine lokalen Jobs gefunden"
message={
hasChanges
? "Keine lokalen Jobs für deine aktuellen Filter. Versuche, die Filter anzupassen."
Expand Down Expand Up @@ -430,7 +431,7 @@ function MobileTab({
onClick={onClick}
data-active={active}
className={cn(
"jobs-mobile-tab relative flex items-center gap-1 whitespace-nowrap rounded-lg border px-2 py-2 text-[11px] font-semibold transition-colors sm:gap-1.5 sm:rounded-xl sm:px-3 sm:text-xs",
"jobs-mobile-tab relative flex items-center gap-1 whitespace-nowrap rounded-full border px-2 py-2 text-[11px] font-semibold transition-colors sm:gap-1.5 sm:rounded-full sm:px-3 sm:text-xs",
active ? activeClass : "text-slate-400 hover:text-slate-200 hover:bg-white/5 border-transparent"
)}
>
Expand Down Expand Up @@ -515,12 +516,12 @@ function EmptyState({
message: string;
}) {
return (
<div className="jobs-empty-copy flex flex-col items-center justify-center space-y-4 px-4 py-8">
<div className="jobs-empty-icon flex h-16 w-16 items-center justify-center rounded-2xl border border-white/[0.05] bg-slate-900/60 text-indigo-400/80">
<Icon size={26} className="opacity-80" />
<div className="jobs-empty-copy flex flex-col items-center justify-center space-y-3 px-4 py-8">
<div className="jobs-empty-icon flex h-12 w-12 items-center justify-center rounded-[0.95rem] border border-white/[0.05] bg-slate-900/40 text-indigo-300/50">
<Icon size={20} className="opacity-[0.62]" />
</div>
<div className="text-center space-y-1.5">
<h3 className="jobs-empty-title text-xl font-bold tracking-tight text-white">{title}</h3>
<div className="space-y-1.5 text-center">
<h3 className="jobs-empty-title text-lg font-bold leading-tight tracking-tight text-white">{title}</h3>
<p className="jobs-empty-message mx-auto max-w-sm text-sm leading-relaxed text-slate-400">{message}</p>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/components/jobs/JobsListSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ export const JobsListSection = memo(function JobsListSection({
</div>
)}
<span className="truncate">{title}</span>
<span className="jobs-section-count rounded-full bg-white/10 px-3 py-1 text-sm font-semibold tracking-normal text-slate-400">
<span className="jobs-section-count inline-flex h-8 min-w-8 items-center justify-center rounded-full bg-white/10 px-0 py-0 text-sm font-semibold tracking-normal text-slate-400 tabular-nums">
{jobs.length}
</span>
</h2>
</div>

<div className="jobs-card-grid grid grid-cols-1 gap-6 lg:grid-cols-2">
{jobs.length === 0 ? (
<div className="jobs-empty-state col-span-full rounded-[1.35rem] border border-white/10 bg-white/[0.025] py-10 text-center">
<div className="text-slate-400">{emptyMsg}</div>
<div className="jobs-empty-state col-span-full flex min-h-[17.5rem] items-center justify-center overflow-hidden rounded-[1.5rem] border-0 px-5 py-10 text-center">
<div className="w-full">{emptyMsg}</div>
</div>
) : (
jobs.map(job => (
Expand Down
Loading