* feat(onboarding): illustrated halftone backdrop from marketing-site art Ports the gnubok-website halftone illustration set into the onboarding flow so signup -> app feels like one product: - OnboardingBackdrop: petal field across the paper background (radially masked to stay calm behind the form), Stockholm skyline dissolving into the bottom edge, two clouds drifting at reading pace that bounce off the viewport and the onboarding panel (IllustrationFloaters, physics ported from the website's BouncingFloaters). - Per-step instrument ghosted in white ink on the dark card header: pencil -> notebook -> adding machine -> calculator, one per step. - Dark mode via invert/hue-rotate filters; prefers-reduced-motion parks the floaters; all art is aria-hidden and pointer-events-none. Applies to /onboarding, /onboarding/agent and /select-company via the shared (onboarding) layout. No new strings, no API changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(onboarding): strip backdrop to skyline + step art per founder review Founder kept the Stadshuset skyline and the per-step header instruments; the petal field and drifting clouds are cut. Removes the now-unused floater physics component and the petals/cloud assets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
import Link from 'next/link'
|
|
import { Settings } from 'lucide-react'
|
|
import { createClient, createServiceClient } from '@/lib/supabase/server'
|
|
import OnboardingBackdrop from '@/components/onboarding/OnboardingBackdrop'
|
|
|
|
export default async function OnboardingLayout({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode
|
|
}) {
|
|
const supabase = await createClient()
|
|
const { data: { user } } = await supabase.auth.getUser()
|
|
|
|
// Only show the settings escape hatch for users who have completed
|
|
// onboarding at least once: i.e. they have a company_members row, even if
|
|
// it points to an archived company. Absolute first-time users don't need
|
|
// it and it clutters the welcome screen.
|
|
//
|
|
// Must use the service client: RLS on company_members goes through
|
|
// user_company_ids(), which filters out archived companies, so an
|
|
// authenticated query would return nothing for a user who archived their
|
|
// last company and make the escape hatch disappear exactly when it's
|
|
// needed most. Scoped to user_id = user.id, so no cross-user exposure.
|
|
let hasCompletedOnboarding = false
|
|
if (user) {
|
|
const service = createServiceClient()
|
|
const { data } = await service
|
|
.from('company_members')
|
|
.select('company_id')
|
|
.eq('user_id', user.id)
|
|
.limit(1)
|
|
.maybeSingle()
|
|
hasCompletedOnboarding = !!data
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background flex items-center justify-center">
|
|
<OnboardingBackdrop />
|
|
|
|
<div className="relative z-10 w-full max-w-lg px-5">
|
|
{children}
|
|
</div>
|
|
|
|
{/* Escape hatch: a user who archived their last company can still
|
|
reach account settings (and the delete-account flow) from here.
|
|
Hidden for absolute first-time users (no memberships ever). */}
|
|
{user && hasCompletedOnboarding && (
|
|
<Link
|
|
href="/settings/account"
|
|
aria-label="Kontoinställningar"
|
|
title="Kontoinställningar"
|
|
className="fixed bottom-6 right-6 z-50 flex h-10 w-10 items-center justify-center rounded-full border border-border bg-background/80 text-muted-foreground shadow-sm backdrop-blur transition-colors hover:border-foreground/40 hover:text-foreground"
|
|
>
|
|
<Settings className="h-4 w-4" />
|
|
</Link>
|
|
)}
|
|
|
|
</div>
|
|
)
|
|
}
|