Skip to content
Open
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
24 changes: 0 additions & 24 deletions apps/app/src/actions/sidebar.ts

This file was deleted.

23 changes: 8 additions & 15 deletions apps/app/src/app/(app)/[orgId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { AnimatedLayout } from '@/components/animated-layout';
import { AppSidebar } from '@/components/app-sidebar';
import { CheckoutCompleteDialog } from '@/components/dialogs/checkout-complete-dialog';
import { Header } from '@/components/header';
import { AssistantSheet } from '@/components/sheets/assistant-sheet';
import { Sidebar } from '@/components/sidebar';
import { TriggerTokenProvider } from '@/components/trigger-token-provider';
import { SidebarProvider } from '@/context/sidebar-context';
import { auth } from '@/utils/auth';
import { SidebarInset, SidebarProvider } from '@comp/ui/sidebar';
import { db, Role } from '@db';
import dynamic from 'next/dynamic';
import { cookies, headers } from 'next/headers';
Expand All @@ -15,7 +14,6 @@ import { ConditionalOnboardingTracker } from './components/ConditionalOnboarding
import { ConditionalPaddingWrapper } from './components/ConditionalPaddingWrapper';
import { DynamicMinHeight } from './components/DynamicMinHeight';

// Helper to safely parse comma-separated roles string
function parseRolesString(rolesStr: string | null | undefined): Role[] {
if (!rolesStr) return [];
return rolesStr
Expand All @@ -38,10 +36,9 @@ export default async function Layout({
const { orgId: requestedOrgId } = await params;

const cookieStore = await cookies();
const isCollapsed = cookieStore.get('sidebar-collapsed')?.value === 'true';
let publicAccessToken = cookieStore.get('publicAccessToken')?.value || undefined;
const defaultOpen = cookieStore.get('sidebar_state')?.value !== 'false';
const publicAccessToken = cookieStore.get('publicAccessToken')?.value || undefined;

// Check if user has access to this organization
const session = await auth.api.getSession({
headers: await headers(),
});
Expand All @@ -51,13 +48,11 @@ export default async function Layout({
return redirect('/auth');
}

// First check if the organization exists and load access flags
const organization = await db.organization.findUnique({
where: { id: requestedOrgId },
});

if (!organization) {
// Organization doesn't exist
return redirect('/auth/not-found');
}

Expand All @@ -70,7 +65,6 @@ export default async function Layout({
});

if (!member) {
// User doesn't have access to this organization
return redirect('/auth/unauthorized');
}

Expand All @@ -82,12 +76,10 @@ export default async function Layout({
return redirect('/no-access');
}

// If this org is not accessible on current plan, redirect to upgrade
if (!organization.hasAccess) {
return redirect(`/upgrade/${organization.id}`);
}

// If onboarding is required and user isn't already on onboarding, redirect
if (!organization.onboardingCompleted) {
return redirect(`/onboarding/${organization.id}`);
}
Expand All @@ -103,8 +95,9 @@ export default async function Layout({
triggerJobId={onboarding?.triggerJobId || undefined}
initialToken={publicAccessToken || undefined}
>
<SidebarProvider initialIsCollapsed={isCollapsed}>
<AnimatedLayout sidebar={<Sidebar organization={organization} />} isCollapsed={isCollapsed}>
<SidebarProvider defaultOpen={defaultOpen}>
<AppSidebar organization={organization} />
<SidebarInset>
{onboarding?.triggerJobId && <ConditionalOnboardingTracker onboarding={onboarding} />}
<Header organizationId={organization.id} />
<ConditionalPaddingWrapper>
Expand All @@ -114,7 +107,7 @@ export default async function Layout({
<Suspense fallback={null}>
<CheckoutCompleteDialog orgId={organization.id} />
</Suspense>
</AnimatedLayout>
</SidebarInset>
<HotKeys />
</SidebarProvider>
</TriggerTokenProvider>
Expand Down
9 changes: 6 additions & 3 deletions apps/app/src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { JwtTokenManager } from '@/components/auth/jwt-token-manager';
import { env } from '@/env.mjs';
import { AnalyticsProvider } from '@comp/analytics';
import { Toaster } from '@comp/ui/sooner';
import { TooltipProvider } from '@comp/ui/tooltip';
import { GoogleTagManager } from '@next/third-parties/google';
import {
defaultShouldDehydrateQuery,
Expand Down Expand Up @@ -86,9 +87,11 @@ export function Providers({ children, session }: ProviderProps) {
userId={session?.user?.id ?? undefined}
userEmail={session?.user?.email ?? undefined}
>
<JwtTokenManager />
{children}
<Toaster richColors />
<TooltipProvider delayDuration={0}>
<JwtTokenManager />
{children}
<Toaster richColors />
</TooltipProvider>
</AnalyticsProvider>
</ThemeProvider>
</QueryClientProvider>
Expand Down
25 changes: 0 additions & 25 deletions apps/app/src/components/animated-layout.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ import { getOrganizations } from '@/data/getOrganizations';
import { auth } from '@/utils/auth';
import { GetObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { cn } from '@comp/ui/cn';
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader,
SidebarRail,
} from '@comp/ui/sidebar';
import { db, type Organization, Role } from '@db';
import { cookies, headers } from 'next/headers';
import { headers } from 'next/headers';
import { MainMenu } from './main-menu';
import { OrganizationSwitcher } from './organization-switcher';
import { SidebarCollapseButton } from './sidebar-collapse-button';
import { SidebarLogo } from './sidebar-logo';

// Helper to safely parse comma-separated roles string
function parseRolesString(rolesStr: string | null | undefined): Role[] {
if (!rolesStr) return [];
return rolesStr
Expand All @@ -21,18 +26,9 @@ function parseRolesString(rolesStr: string | null | undefined): Role[] {
.filter((r) => r in Role) as Role[];
}

export async function Sidebar({
organization,
collapsed = false,
}: {
organization: Organization | null;
collapsed?: boolean;
}) {
const cookieStore = await cookies();
const isCollapsed = collapsed || cookieStore.get('sidebar-collapsed')?.value === 'true';
export async function AppSidebar({ organization }: { organization?: Organization | null }) {
const { organizations } = await getOrganizations();

// Generate logo URLs for all organizations
const logoUrls: Record<string, string> = {};
if (s3Client && APP_AWS_ORG_ASSETS_BUCKET) {
await Promise.all(
Expand All @@ -52,7 +48,6 @@ export async function Sidebar({
);
}

// Check feature flags for menu items
const session = await auth.api.getSession({
headers: await headers(),
});
Expand All @@ -78,41 +73,38 @@ export async function Sidebar({
if (member?.role) {
const roles = parseRolesString(member.role);
hasAuditorRole = roles.includes(Role.auditor);
// Only hide tabs if auditor is their ONLY role
// If they have multiple roles (e.g., "owner, auditor" or "admin, auditor"), show tabs
isOnlyAuditor = hasAuditorRole && roles.length === 1;
}
}

return (
<div className="bg-card flex h-full flex-col gap-0 overflow-x-clip">
<div className="flex flex-col gap-2 p-4">
<div className={cn('flex items-center justify-start', isCollapsed && 'justify-center')}>
<SidebarLogo isCollapsed={isCollapsed} />
</div>
<div className="mt-2 flex flex-col gap-2">
<Sidebar collapsible="icon" variant="sidebar" side="left">
<SidebarHeader>
<SidebarLogo />
<div className="mt-2">
<OrganizationSwitcher
organizations={organizations}
organization={organization}
isCollapsed={isCollapsed}
organization={organization ?? null}
logoUrls={logoUrls}
/>
<MainMenu
organizationId={organization?.id ?? ''}
organization={organization}
isCollapsed={isCollapsed}
isQuestionnaireEnabled={isQuestionnaireEnabled}
isTrustNdaEnabled={isTrustNdaEnabled}
hasAuditorRole={hasAuditorRole}
isOnlyAuditor={isOnlyAuditor}
/>
</div>
</div>
<div className="flex-1" />
</SidebarHeader>

<SidebarContent className="px-2">
<MainMenu
organizationId={organization?.id ?? ''}
organization={organization ?? null}
isQuestionnaireEnabled={isQuestionnaireEnabled}
isTrustNdaEnabled={isTrustNdaEnabled}
hasAuditorRole={hasAuditorRole}
isOnlyAuditor={isOnlyAuditor}
/>
</SidebarContent>

<div className="flex justify-center py-2">
<SidebarCollapseButton isCollapsed={isCollapsed} />
</div>
</div>
<SidebarFooter>
<SidebarCollapseButton />
</SidebarFooter>
<SidebarRail />
</Sidebar>
);
}
28 changes: 2 additions & 26 deletions apps/app/src/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { getFeatureFlags } from '@/app/posthog';
import { UserMenu } from '@/components/user-menu';
import { getOrganizations } from '@/data/getOrganizations';
import { auth } from '@/utils/auth';
import { Skeleton } from '@comp/ui/skeleton';
import { headers } from 'next/headers';
import { SidebarTrigger } from '@comp/ui/sidebar';
import { Suspense } from 'react';
import { AssistantButton } from './ai/chat-button';
import { MobileMenu } from './mobile-menu';
import { NotificationBell } from './notifications/notification-bell';

export async function Header({
Expand All @@ -16,29 +12,9 @@ export async function Header({
organizationId?: string;
hideChat?: boolean;
}) {
const { organizations } = await getOrganizations();

// Check feature flags for menu items
const session = await auth.api.getSession({
headers: await headers(),
});
let isQuestionnaireEnabled = false;
let isTrustNdaEnabled = false;
if (session?.user?.id) {
const flags = await getFeatureFlags(session.user.id);
isQuestionnaireEnabled = flags['ai-vendor-questionnaire'] === true;
isTrustNdaEnabled =
flags['is-trust-nda-enabled'] === true || flags['is-trust-nda-enabled'] === 'true';
}

return (
<header className="border/40 sticky top-0 z-10 flex items-center justify-between border-b px-4 py-2 backdrop-blur-sm bg-card">
<MobileMenu
organizations={organizations}
organizationId={organizationId}
isQuestionnaireEnabled={isQuestionnaireEnabled}
isTrustNdaEnabled={isTrustNdaEnabled}
/>
<SidebarTrigger className="md:hidden" />

{!hideChat && <AssistantButton />}

Expand Down
Loading