diff --git a/apps/app/src/actions/organization/get-organization-users-action.ts b/apps/app/src/actions/organization/get-organization-users-action.ts index 053c9ae0b8..451d018e81 100644 --- a/apps/app/src/actions/organization/get-organization-users-action.ts +++ b/apps/app/src/actions/organization/get-organization-users-action.ts @@ -26,26 +26,32 @@ export const getOrganizationUsersAction = authActionClient } try { - const users = await db.user.findMany({ + const users = await db.organizationMember.findMany({ where: { organizationId: ctx.user.organizationId, }, select: { - id: true, - name: true, - image: true, + user: { + select: { + id: true, + name: true, + image: true, + }, + }, }, orderBy: { - name: "asc", + user: { + name: "asc", + }, }, }); return { success: true, - data: users.map((user: User) => ({ - id: user.id, - name: user.name || "", - image: user.image || "", + data: users.map((user) => ({ + id: user.user.id, + name: user.user.name || "", + image: user.user.image || "", })), }; } catch (error) { diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/(home)/layout.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/(home)/layout.tsx index 14409c5c48..328828257b 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/(home)/layout.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/(home)/layout.tsx @@ -1,6 +1,5 @@ import { getI18n } from "@/locales/server"; import { SecondaryMenu } from "@bubba/ui/secondary-menu"; -import { Suspense } from "react"; export default async function Layout({ children, @@ -11,9 +10,7 @@ export default async function Layout({ return (
- Loading...
}> - - +
{children}
diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/(home)/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/(home)/page.tsx index 2a73303833..ba6b6a2c5e 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/(home)/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/(home)/page.tsx @@ -1,24 +1,8 @@ import { auth } from "@/auth"; import { FrameworkProgress } from "@/components/charts/framework-progress"; import { RequirementStatus } from "@/components/charts/requirement-status"; - -import { SkeletonLoader } from "@/components/skeleton-loader"; - import { db } from "@bubba/db"; import { redirect } from "next/navigation"; -import { Suspense } from "react"; - -async function getComplianceOverview(organizationId: string) { - const frameworks = await db.organizationFramework.findMany({ - where: { organizationId }, - include: { - organizationControl: true, - framework: true, - }, - }); - - return { frameworks }; -} export default async function DashboardPage() { const session = await auth(); @@ -32,13 +16,23 @@ export default async function DashboardPage() { ); return ( - }> -
-
- - -
+
+
+ +
- +
); } + +async function getComplianceOverview(organizationId: string) { + const frameworks = await db.organizationFramework.findMany({ + where: { organizationId }, + include: { + organizationControl: true, + framework: true, + }, + }); + + return { frameworks }; +} diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/layout.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/layout.tsx index eb25e20991..0e53af7c23 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/layout.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/layout.tsx @@ -2,10 +2,8 @@ import { auth } from "@/auth"; import { Title } from "@/components/title"; import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; -import { SecondaryMenu } from "@bubba/ui/secondary-menu"; import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; -import { Suspense } from "react"; export default async function Layout({ children, @@ -33,13 +31,21 @@ export default async function Layout({ } return ( -
+
+ + + <main className="h-[calc(100vh-4rem-4rem)]">{children}</main> + </div> + ); + + /* return ( + <div className="h-[calc(100vh-4rem)] bg-background max-w-[1200px]"> <div className="h-16 px-4 border-b flex items-center"> <Title title={policy.name} href="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/policies/all" /> </div> <main className="h-[calc(100vh-4rem-4rem)]">{children}</main> </div> - ); + ); */ } const getPolicy = unstable_cache( diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/settings/layout.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/settings/layout.tsx index 07ac2611d8..7103268daa 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/settings/layout.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/settings/layout.tsx @@ -14,7 +14,11 @@ export default async function Layout({ items={[ { path: "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/settings", label: t("settings.general.title") }, { path: "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/settings/members", label: t("settings.members.title") }, - { path: "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/settings/billing", label: t("settings.billing.title") }, + { + path: "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/settings/billing", + label: t("settings.billing.title"), + enabled: false, + }, ]} /> </Suspense> diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/settings/members/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/settings/members/page.tsx new file mode 100644 index 0000000000..f9dbba63c0 --- /dev/null +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/settings/members/page.tsx @@ -0,0 +1,9 @@ +import { TeamMembers } from "@/components/settings/team/team-members"; + +export default async function Members() { + return ( + <div className="space-y-4 sm:space-y-8"> + <TeamMembers /> + </div> + ); +} diff --git a/apps/app/src/auth/config.ts b/apps/app/src/auth/config.ts index 5d5ca9ba21..65319f21ae 100644 --- a/apps/app/src/auth/config.ts +++ b/apps/app/src/auth/config.ts @@ -62,10 +62,10 @@ export const authConfig: NextAuthConfig = { await db.user.update({ where: { id: user.id, - organizationId: user.organizationId, }, data: { lastLogin: new Date(), + organizationId: user.organizationId, }, }); } diff --git a/apps/app/src/auth/org.ts b/apps/app/src/auth/org.ts index ca5a15605a..e70f468d7d 100644 --- a/apps/app/src/auth/org.ts +++ b/apps/app/src/auth/org.ts @@ -35,9 +35,10 @@ export async function createOrganizationAndConnectUser(input: { name: initialName, tier: "free", website: "", - users: { - connect: { - id: input.userId, + members: { + create: { + userId: input.userId, + role: "admin", }, }, }, @@ -69,5 +70,10 @@ export async function createOrganizationAndConnectUser(input: { data: { stripeCustomerId }, }); + await db.user.update({ + where: { id: input.userId }, + data: { organizationId: organization.id }, + }); + return organization.id; } diff --git a/apps/app/src/auth/stripe.ts b/apps/app/src/auth/stripe.ts index 3740b35c1e..a6efe664d8 100644 --- a/apps/app/src/auth/stripe.ts +++ b/apps/app/src/auth/stripe.ts @@ -11,5 +11,5 @@ if (!process.env.STRIPE_WEBHOOK_SECRET) { export const stripeWebhookSecret = process.env.STRIPE_WEBHOOK_SECRET; export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { - apiVersion: "2024-12-18.acacia", + apiVersion: "2025-01-27.acacia", }); diff --git a/apps/app/src/components/settings/team/team-members.tsx b/apps/app/src/components/settings/team/team-members.tsx new file mode 100644 index 0000000000..d171e5c5f9 --- /dev/null +++ b/apps/app/src/components/settings/team/team-members.tsx @@ -0,0 +1,21 @@ +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@bubba/ui/tabs"; +import { Suspense } from "react"; + +export function TeamMembers() { + return ( + <Tabs defaultValue="members"> + <TabsList className="bg-transparent border-b-[1px] w-full justify-start rounded-none mb-1 p-0 h-auto pb-4"> + <TabsTrigger value="members" className="p-0 m-0 mr-4"> + Team Members + </TabsTrigger> + <TabsTrigger value="pending" className="p-0 m-0"> + Pending Invitations + </TabsTrigger> + </TabsList> + + <TabsContent value="members">Test</TabsContent> + + <TabsContent value="pending">Test 2</TabsContent> + </Tabs> + ); +} diff --git a/apps/app/src/components/tables/members/index.tsx b/apps/app/src/components/tables/members/index.tsx new file mode 100644 index 0000000000..c1fea0e175 --- /dev/null +++ b/apps/app/src/components/tables/members/index.tsx @@ -0,0 +1,11 @@ +import { getOrganizationUsersAction } from "@/actions/organization/get-organization-users-action"; +import { auth } from "@/auth"; +import { db } from "@bubba/db"; + +export async function MembersTable() { + const session = await auth(); + + const members = await getOrganizationUsersAction(); + + return <div>MembersTable</div>; +} diff --git a/apps/web/src/app/(home)/page.tsx b/apps/web/src/app/(home)/page.tsx index a6497b438a..bc3c80df45 100644 --- a/apps/web/src/app/(home)/page.tsx +++ b/apps/web/src/app/(home)/page.tsx @@ -42,7 +42,7 @@ export default function Home() { <div className="mt-12 w-full max-w-[800px]"> <div className="mt-8 text-center"> <Link - href="https://discord.gg/3JgpACjae6" + href="https://discord.gg/compai" className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-primary/10 hover:bg-primary/20 transition-colors text-primary" > Join us on Discord diff --git a/apps/web/src/app/(legal)/privacy/page.tsx b/apps/web/src/app/(home)/privacy/page.tsx similarity index 77% rename from apps/web/src/app/(legal)/privacy/page.tsx rename to apps/web/src/app/(home)/privacy/page.tsx index 45091f750b..c5611ea129 100644 --- a/apps/web/src/app/(legal)/privacy/page.tsx +++ b/apps/web/src/app/(home)/privacy/page.tsx @@ -12,11 +12,11 @@ export default function PrivacyPage() { <section> <div className="container mx-auto px-4"> <div className="flex flex-col items-center py-16 md:py-24"> - <h1 className="text-4xl md:text-5xl font-bold leading-tight tracking-tighter lg:leading-[1.1] max-w-[800px] mx-auto text-center bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent mb-16"> + <h1 className="text-4xl md:text-5xl font-bold leading-tight tracking-tighter lg:leading-[1.1] max-w-[800px] mx-auto bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent"> <Balancer>Privacy Policy</Balancer> </h1> - <div className="w-full max-w-[800px]"> + <div className="mt-10 w-full max-w-[800px]"> <PrivacyContent /> </div> </div> diff --git a/apps/web/src/app/(legal)/terms/page.tsx b/apps/web/src/app/(home)/terms/page.tsx similarity index 76% rename from apps/web/src/app/(legal)/terms/page.tsx rename to apps/web/src/app/(home)/terms/page.tsx index eb583e4555..0f8badbf72 100644 --- a/apps/web/src/app/(legal)/terms/page.tsx +++ b/apps/web/src/app/(home)/terms/page.tsx @@ -12,11 +12,11 @@ export default function TermsPage() { <section> <div className="container mx-auto px-4"> <div className="flex flex-col items-center py-16 md:py-24"> - <h1 className="text-4xl md:text-5xl font-bold leading-tight tracking-tighter lg:leading-[1.1] max-w-[800px] mx-auto text-center bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent mb-16"> + <h1 className="text-4xl md:text-5xl font-bold leading-tight tracking-tighter lg:leading-[1.1] max-w-[800px] mx-auto bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent"> <Balancer>Terms of Service</Balancer> </h1> - <div className="w-full max-w-[800px]"> + <div className="mt-10 w-full max-w-[800px]"> <TermsContent /> </div> </div> diff --git a/apps/web/src/app/(legal)/layout.tsx b/apps/web/src/app/(legal)/layout.tsx deleted file mode 100644 index f4a26b8e22..0000000000 --- a/apps/web/src/app/(legal)/layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { SiteFooter } from "@/app/components/site-footer"; -import { SiteHeader } from "@/app/components/site-header"; - -export default function LegalLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( - <div className="flex min-h-svh flex-col"> - <SiteHeader /> - <main className="flex-1">{children}</main> - <SiteFooter /> - </div> - ); -} diff --git a/apps/web/src/app/components/legal/privacy-content.tsx b/apps/web/src/app/components/legal/privacy-content.tsx index 7943716d5e..30086d0f1f 100644 --- a/apps/web/src/app/components/legal/privacy-content.tsx +++ b/apps/web/src/app/components/legal/privacy-content.tsx @@ -1,6 +1,6 @@ export function PrivacyContent() { return ( - <div className="prose prose-gray dark:prose-invert max-w-none"> + <div className="prose prose-gray dark:prose-invert overflow-y-auto scrollbar-hide"> <p className="text-lg text-muted-foreground"> Last updated: February 1, 2025 </p> diff --git a/apps/web/src/app/components/legal/terms-content.tsx b/apps/web/src/app/components/legal/terms-content.tsx index cd556d40f3..8cd7c226ab 100644 --- a/apps/web/src/app/components/legal/terms-content.tsx +++ b/apps/web/src/app/components/legal/terms-content.tsx @@ -1,6 +1,6 @@ export function TermsContent() { return ( - <div className="prose prose-gray dark:prose-invert max-w-none"> + <div className="prose prose-gray dark:prose-invert overflow-y-auto scrollbar-hide"> <p className="text-lg text-muted-foreground"> Last updated: February 1, 2025 </p> @@ -8,10 +8,10 @@ export function TermsContent() { <section className="mt-12"> <h2 className="text-3xl font-semibold mb-6">1. Agreement to Terms</h2> <p className="text-lg text-muted-foreground"> - By accessing or using Bubba AI, Inc.'s services, you agree to be bound - by these Terms of Service and all applicable laws and regulations. If - you do not agree with any of these terms, you are prohibited from - using or accessing our services. + By accessing or using Comp AI's services, you agree to be bound by + these Terms of Service and all applicable laws and regulations. If you + do not agree with any of these terms, you are prohibited from using or + accessing our services. </p> </section> @@ -22,7 +22,7 @@ export function TermsContent() { non-exclusive, non-transferable license to use our services for your organization's internal business purposes. </p> - <h3 className="text-2xl font-medium mb-4">2.1 Restrictions</h3> + <h3 className="text-2xl font-medium mb-4 mt-6">2.1 Restrictions</h3> <p className="text-lg text-muted-foreground">You may not:</p> <ul className="space-y-2 list-disc pl-6 text-lg text-muted-foreground"> <li>Modify or copy our materials without explicit permission</li> diff --git a/apps/web/src/app/components/waitlist-form.tsx b/apps/web/src/app/components/waitlist-form.tsx index 3203ce5fe7..02953154d0 100644 --- a/apps/web/src/app/components/waitlist-form.tsx +++ b/apps/web/src/app/components/waitlist-form.tsx @@ -56,7 +56,7 @@ export function WaitlistForm() { Thanks, you're on the list! </p> <Link - href="https://discord.gg/3JgpACjae6" + href="https://discord.gg/compai" className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-primary/10 hover:bg-primary/20 transition-colors text-primary" > Join Community diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css deleted file mode 100644 index 42b49d7aeb..0000000000 --- a/apps/web/src/app/globals.css +++ /dev/null @@ -1,86 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -body { - font-family: Arial, Helvetica, sans-serif; -} - -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 0 0% 3.9%; - --card: 0 0% 100%; - --card-foreground: 0 0% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - --primary: 0 0% 9%; - --primary-foreground: 0 0% 98%; - --secondary: 0 0% 96.1%; - --secondary-foreground: 0 0% 9%; - --muted: 0 0% 96.1%; - --muted-foreground: 0 0% 45.1%; - --accent: 0 0% 96.1%; - --accent-foreground: 0 0% 9%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 89.8%; - --input: 0 0% 89.8%; - --ring: 0 0% 3.9%; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - --radius: 0.5rem; - } - .dark { - --background: 0 0% 3.9%; - --foreground: 0 0% 98%; - --card: 0 0% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 0 0% 9%; - --secondary: 0 0% 14.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 14.9%; - --muted-foreground: 0 0% 63.9%; - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 14.9%; - --input: 0 0% 14.9%; - --ring: 0 0% 83.1%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} - -@layer components { - .border-grid { - @apply border-border; - } - - .container-wrapper { - @apply w-full; - } - - .container { - @apply mx-auto max-w-7xl px-4 sm:px-6 lg:px-8; - } -} diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 3a18c35798..8637685851 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,24 +1,16 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Geist } from "next/font/google"; import { Toaster } from "sonner"; -import "@/styles/globals.css"; import "@bubba/ui/globals.css"; import { ThemeProvider } from "@/app/providers"; import { cn } from "@/lib/utils"; import { GoogleTagManager } from "@next/third-parties/google"; -import { SiteFooter } from "./components/site-footer"; -import { SiteHeader } from "./components/site-header"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - export const metadata: Metadata = { title: "Comp AI", description: "Automate SOC 2, ISO 27001 and GDPR compliance with AI.", diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 2cdc0b691e..4d2919a493 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -76,25 +76,27 @@ model User { lastLogin DateTime? organizationId String? - organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade) - - accounts Account[] - sessions Session[] - Risk Risk[] - RiskMitigationTask RiskMitigationTask[] - RiskComment RiskComment[] - AuditLog AuditLog[] - TaskAttachment TaskAttachment[] - TaskComments TaskComments[] - Artifact Artifact[] - VendorComment VendorComment[] - VendorMitigationTask VendorMitigationTask[] - VendorTaskComments VendorTaskComments[] - VendorTaskAttachment VendorTaskAttachment[] - Vendors Vendors[] - VendorRiskAssessment VendorRiskAssessment[] - RiskAttachment RiskAttachment[] - + organization Organization? @relation("CurrentOrganization", fields: [organizationId], references: [id], onDelete: SetNull) + + accounts Account[] + sessions Session[] + Risk Risk[] + RiskMitigationTask RiskMitigationTask[] + RiskComment RiskComment[] + AuditLog AuditLog[] + TaskAttachment TaskAttachment[] + TaskComments TaskComments[] + Artifact Artifact[] + VendorComment VendorComment[] + VendorMitigationTask VendorMitigationTask[] + VendorTaskComments VendorTaskComments[] + VendorTaskAttachment VendorTaskAttachment[] + Vendors Vendors[] + VendorRiskAssessment VendorRiskAssessment[] + RiskAttachment RiskAttachment[] + organizationMemberships OrganizationMember[] + + @@index([email]) @@index([organizationId]) } @@ -106,7 +108,8 @@ model Organization { tier Tier @default(free) policiesCreated Boolean @default(false) - users User[] + users User[] @relation("CurrentOrganization") + Risk Risk[] AuditLog AuditLog[] RiskMitigationTask RiskMitigationTask[] @@ -128,6 +131,7 @@ model Organization { VendorRiskAssessment VendorRiskAssessment[] VendorContact VendorContact[] RiskAttachment RiskAttachment[] + members OrganizationMember[] @@index([stripeCustomerId]) } @@ -689,3 +693,30 @@ model RiskAttachment { @@index([riskId]) @@index([organizationId]) } + +enum MembershipRole { + owner + admin + member + viewer +} + +model OrganizationMember { + id String @id @default(cuid()) + role MembershipRole @default(member) + invitedEmail String? + accepted Boolean @default(false) + department Departments @default(none) + joinedAt DateTime @default(now()) + lastActive DateTime? + + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + organizationId String + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + + @@unique([userId, organizationId]) + @@index([organizationId]) + @@index([userId]) +} diff --git a/packages/ui/src/components/secondary-menu.tsx b/packages/ui/src/components/secondary-menu.tsx index d7374b04e7..8717b6ff95 100644 --- a/packages/ui/src/components/secondary-menu.tsx +++ b/packages/ui/src/components/secondary-menu.tsx @@ -1,11 +1,22 @@ "use client"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@bubba/ui/tooltip"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { cn } from "../utils"; interface SecondaryMenuProps { - items: { path: string; label: string; query?: Record<string, string> }[]; + items: { + enabled?: boolean; + path: string; + label: string; + query?: Record<string, string>; + }[]; isChild?: boolean; } @@ -36,22 +47,46 @@ export function SecondaryMenu({ items, isChild }: SecondaryMenuProps) { isChild ? "space-x-3" : "space-x-6", )} > - {items.map((item) => ( - <Link - prefetch - key={item.path} - href={{ - pathname: item.path, - }} - className={cn( - "text-muted-foreground", - isActiveLink(item.path) && - "font-medium text-primary underline underline-offset-8", - )} - > - <span>{item.label}</span> - </Link> - ))} + {items.map((item) => { + const isDisabled = item.enabled === false; + + const itemContent = ( + <span + className={cn( + "text-muted-foreground", + isActiveLink(item.path) && + "font-medium text-primary underline underline-offset-8", + isDisabled && "opacity-50 cursor-pointer", + )} + > + {item.label} + </span> + ); + + return ( + <li key={item.path}> + {isDisabled ? ( + <TooltipProvider> + <Tooltip> + <TooltipTrigger asChild>{itemContent}</TooltipTrigger> + <TooltipContent> + <p>Coming soon</p> + </TooltipContent> + </Tooltip> + </TooltipProvider> + ) : ( + <Link + prefetch + href={{ + pathname: item.path, + }} + > + {itemContent} + </Link> + )} + </li> + ); + })} </ul> </nav> ); diff --git a/packages/ui/src/components/tabs.tsx b/packages/ui/src/components/tabs.tsx index 0c8912a272..5604e4dc47 100644 --- a/packages/ui/src/components/tabs.tsx +++ b/packages/ui/src/components/tabs.tsx @@ -14,7 +14,7 @@ const TabsList = React.forwardRef< ref={ref} className={cn( "inline-flex h-10 items-center justify-center bg-accent p-1 text-muted-foreground", - className + className, )} {...props} /> @@ -29,7 +29,7 @@ const TabsTrigger = React.forwardRef< ref={ref} className={cn( "inline-flex items-center justify-center whitespace-nowrap px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground", - className + className, )} {...props} /> @@ -44,7 +44,7 @@ const TabsContent = React.forwardRef< ref={ref} className={cn( "mt-2 ring-offset-background focus-visible:outline-none", - className + className, )} {...props} /> diff --git a/packages/ui/src/globals.css b/packages/ui/src/globals.css index da507ee9c3..e290c59c21 100644 --- a/packages/ui/src/globals.css +++ b/packages/ui/src/globals.css @@ -80,6 +80,21 @@ --novel-highlight-gray: #3a3a3a; } + +@layer components { + .border-grid { + @apply border-border; + } + + .container-wrapper { + @apply w-full; + } + + .container { + @apply mx-auto max-w-7xl px-4 sm:px-6 lg:px-8; + } +} + .ProseMirror ol { @apply my-4 ml-6; } @@ -601,3 +616,14 @@ pre { scrollbar-width: thin; scrollbar-color: var(--muted-foreground) transparent; } + +@layer utilities { + .scrollbar-hide { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } + + .scrollbar-hide::-webkit-scrollbar { + display: none; /* Chrome, Safari and Opera */ + } +} \ No newline at end of file