Skip to content
Merged
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
2 changes: 1 addition & 1 deletion apps/app/src/app/(app)/(dashboard)/[orgId]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default async function Layout({
{onboarding?.triggerJobId && (
<OnboardingTracker
onboarding={onboarding}
publicAccessToken={publicAccessToken!}
publicAccessToken={publicAccessToken ?? ""}
/>
)}
<Header />
Expand Down
28 changes: 11 additions & 17 deletions apps/app/src/components/organization-switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,14 @@ export function OrganizationSwitcher({
const [showCreateOrg, setShowCreateOrg] = useState(false);
const [pendingOrgId, setPendingOrgId] = useState<string | null>(null);

const [showOrganizationSwitcher, setShowOrganizationSwitcher] =
useQueryState("showOrganizationSwitcher", {
const [showOrganizationSwitcher, setShowOrganizationSwitcher] = useQueryState(
"showOrganizationSwitcher",
{
history: "push",
parse: (value) => value === "true",
serialize: (value) => value.toString(),
});
},
);

const { execute, status } = useAction(changeOrganizationAction, {
onSuccess: (result) => {
Expand Down Expand Up @@ -218,14 +220,13 @@ export function OrganizationSwitcher({
<DialogTrigger asChild>
<Button
variant="outline"
// biome-ignore lint/a11y/useSemanticElements: <explanation>
role="combobox"
aria-label={"Select Organization"}
className={cn(
"flex justify-between mx-auto rounded-md",
isCollapsed ? "h-min w-min p-0" : "h-10 w-full p-0",
status === "executing"
? "opacity-50 cursor-not-allowed"
: "",
status === "executing" ? "opacity-50 cursor-not-allowed" : "",
)}
disabled={status === "executing"}
>
Expand All @@ -245,9 +246,7 @@ export function OrganizationSwitcher({
</Button>
</DialogTrigger>
<DialogContent className="p-0 sm:max-w-[400px]">
<DialogTitle className="sr-only">
{"Select Organization"}
</DialogTitle>
<DialogTitle className="sr-only">{"Select Organization"}</DialogTitle>
<Command>
<div className="flex items-center border-b px-3">
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
Expand All @@ -257,9 +256,7 @@ export function OrganizationSwitcher({
/>
</div>
<CommandList>
<CommandEmpty>
{"No results found"}
</CommandEmpty>
<CommandEmpty>{"No results found"}</CommandEmpty>
<CommandGroup className="max-h-[300px] overflow-y-auto">
{organizations.map((org) => (
<CommandItem
Expand All @@ -274,8 +271,7 @@ export function OrganizationSwitcher({
}}
disabled={status === "executing"}
>
{status === "executing" &&
pendingOrgId === org.id ? (
{status === "executing" && pendingOrgId === org.id ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : currentOrganization?.id === org.id ? (
<Check className="mr-2 h-4 w-4" />
Expand All @@ -287,9 +283,7 @@ export function OrganizationSwitcher({
isCollapsed={false}
className="mr-2 h-6 w-6"
/>
<span className="truncate">
{getDisplayName(org)}
</span>
<span className="truncate">{getDisplayName(org)}</span>
</CommandItem>
))}
</CommandGroup>
Expand Down
89 changes: 43 additions & 46 deletions apps/app/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,50 @@ import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server";

export const config = {
runtime: "nodejs",
matcher: [
// Skip auth-related routes
"/((?!api|_next/static|_next/image|favicon.ico|monitoring|ingest|onboarding|research).*)",
],
runtime: "nodejs",
matcher: [
// Skip auth-related routes
"/((?!api|_next/static|_next/image|favicon.ico|monitoring|ingest|onboarding|research).*)",
],
};

export async function middleware(request: NextRequest) {
const session = await auth.api.getSession({
headers: await headers(),
});

const response = NextResponse.next();
const nextUrl = request.nextUrl;

// Add x-path-name
response.headers.set("x-pathname", nextUrl.pathname);

// 1. Not authenticated
if (!session && nextUrl.pathname !== "/auth") {
const url = new URL("/auth", request.url);

return NextResponse.redirect(url);
}

// 2. Authenticated; redirect to onboarding if not completed
if (session) {
// 2.1. If the user has an active organization, redirect to implementation
if (
session.session.activeOrganizationId &&
nextUrl.pathname !== "/auth" &&
!nextUrl.pathname.startsWith("/setup/onboarding")
) {
const onboarding = await db.onboarding.findFirst({
where: {
organizationId: session.session
.activeOrganizationId as string,
},
});

if (!onboarding?.completed && !onboarding?.triggerJobId) {
return NextResponse.redirect(
new URL("/setup/onboarding", request.url),
);
}
}
}

return response;
const session = await auth.api.getSession({
headers: await headers(),
});

const response = NextResponse.next();
const nextUrl = request.nextUrl;

// Add x-path-name
response.headers.set("x-pathname", nextUrl.pathname);

// 1. Not authenticated
if (!session && nextUrl.pathname !== "/auth") {
const url = new URL("/auth", request.url);

return NextResponse.redirect(url);
}

// 2. Authenticated; redirect to onboarding if not completed
if (session) {
// 2.1. If the user has an active organization, redirect to implementation
if (
session.session.activeOrganizationId &&
nextUrl.pathname !== "/auth" &&
!nextUrl.pathname.startsWith("/setup/onboarding")
) {
const onboarding = await db.onboarding.findFirst({
where: {
organizationId: session.session.activeOrganizationId as string,
},
});

if (onboarding && !onboarding.completed && !onboarding.triggerJobId) {
return NextResponse.redirect(new URL("/setup/onboarding", request.url));
}
}
}

return response;
}
Loading