Add a client component for post-auth routing (#36)

This commit is contained in:
Chris Estreich 2025-05-18 22:19:26 -07:00 committed by GitHub
parent cfc93622f8
commit dfe41a5da6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 85 additions and 29 deletions

8
.env
View file

@ -14,10 +14,10 @@ NEXT_PUBLIC_CLERK_FRONTEND_API=https://epic-chamois-85.clerk.accounts.dev
# https://clerk.com/docs/deployments/clerk-environment-variables#sign-in-and-sign-up-redirects
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL=/authorized
NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL=/authorized
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/authorized
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/authorized
# ClickHouse
CLICKHOUSE_URL=https://fake.us-west-2.aws.clickhouse.cloud:1234

View file

@ -150,9 +150,9 @@ export async function syncAuth({ userId, orgId, orgRole }: SyncAuth) {
return;
}
syncCurrentUser({ userId, orgId, orgRole });
await syncCurrentUser({ userId, orgId, orgRole });
if (orgId) {
syncOrg(orgId);
await syncOrg(orgId);
}
}

View file

@ -1,7 +0,0 @@
import { UserButton as ClerkUserButton } from '@clerk/nextjs';
export const UserButton = () => (
<div className="absolute top-8 right-8">
<ClerkUserButton />
</div>
);

View file

@ -0,0 +1,55 @@
'use client';
import { useState, useRef, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@clerk/nextjs';
import { LoaderCircle, CircleCheck, CircleX } from 'lucide-react';
import { Card, CardContent } from '@/components/ui';
export const Authorized = () => {
const { isSignedIn, orgId } = useAuth();
const [isLoading, setIsLoading] = useState(true);
const isLoadingRef = useRef(true);
const router = useRouter();
useEffect(() => {
if (typeof isSignedIn !== 'undefined' && isLoadingRef.current) {
isLoadingRef.current = false;
setTimeout(() => setIsLoading(false), 250);
}
}, [isSignedIn]);
useEffect(() => {
if (!isLoading) {
let path = '/dashboard';
if (!isSignedIn) {
path = '/sign-in';
} else if (!orgId) {
path = '/select-org';
}
setTimeout(() => router.push(path), 1000);
}
}, [router, isLoading, isSignedIn, orgId]);
return (
<Card className="max-w-md mx-auto">
<CardContent className="flex flex-col gap-4">
<div className="flex flex-row items-center gap-2">
<div className="font-medium text-sm text-muted-foreground">
Authenticating
</div>
{isLoading ? (
<LoaderCircle className="animate-spin" />
) : isSignedIn ? (
<CircleCheck className="text-lime-500" />
) : (
<CircleX className="text-rose-500" />
)}
</div>
</CardContent>
</Card>
);
};

View file

@ -0,0 +1,5 @@
import { Authorized } from './Authorized';
export default async function Page() {
return <Authorized />;
}

View file

@ -1,5 +1,6 @@
import { Logo } from '@/components/layout';
import { UserButton } from './UserButton';
import { UserButton } from '@clerk/nextjs';
import { Logo, ThemeSwitcher } from '@/components/layout';
export default async function OnboardingLayout({
children,
@ -8,11 +9,16 @@ export default async function OnboardingLayout({
}) {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="flex flex-col gap-8">
<Logo className="self-center" />
{children}
<div className="flex flex-col gap-8">{children}</div>
<div className="absolute top-8 left-8">
<Logo />
</div>
<div className="absolute top-8 right-8">
<div className="flex items-center gap-2">
<ThemeSwitcher />
<UserButton />
</div>
</div>
<UserButton />
</div>
);
}

View file

@ -52,7 +52,7 @@ export default async function RootLayout({
const locale = await getLocale();
setRequestLocale(locale);
syncAuth(await auth());
await syncAuth(await auth());
return (
<html lang={locale} suppressHydrationWarning>

View file

@ -10,14 +10,11 @@ import { cn } from '@/lib/utils';
type LogoProps = Omit<
SVGProps<SVGSVGElement>,
'xmlns' | 'viewBox' | 'onClick' | 'fill'
>;
> & {
scale?: number;
};
export const Logo = ({
width = 50,
height = 32,
className,
...props
}: LogoProps) => {
export const Logo = ({ scale = 0.4, className, ...props }: LogoProps) => {
const router = useRouter();
const { resolvedTheme } = useTheme();
const [fill, setFill] = useState('#000');
@ -31,8 +28,8 @@ export const Logo = ({
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
width={100 * scale}
height={64 * scale}
viewBox="90 12 100 64"
onClick={() => router.push('/')}
className={cn('logo cursor-pointer', className)}