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
12 changes: 4 additions & 8 deletions .cursor/mcp.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
{
"mcpServers": {
"trigger": {
"command": "npx",
"args": [
"trigger.dev@latest",
"mcp",
"--dev-only"
]
"chakra-ui": {
"command": "bunx",
"args": ["-y", "@chakra-ui/react-mcp"]
}
}
}
}
2 changes: 1 addition & 1 deletion apps/api/src/app/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export async function getFleetAgent({
os,
}: {
os: 'macos' | 'windows' | 'linux';
}) {
}): Promise<unknown> {
if (!s3Client) {
throw new Error('S3 client not configured');
}
Expand Down
7 changes: 7 additions & 0 deletions apps/portal/agents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Portal AGENTS.md

## Component usage

- Only use components from the `packages/ui-new` package.
- Import components from `@trycompai/ui-new` (or the appropriate package name).
- Do not use components from `packages/ui` or other UI packages.
6 changes: 5 additions & 1 deletion apps/portal/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import './src/env.mjs';
const isStandalone = process.env.NEXT_OUTPUT_STANDALONE === 'true';

const config = {
transpilePackages: ['@trycompai/db'],
transpilePackages: ['@trycompai/db', '@trycompai/ui-new'],
serverExternalPackages: ['@ark-ui/react', '@zag-js/anatomy'],
experimental: {
optimizePackageImports: ['@chakra-ui/react'],
},
images: {
remotePatterns: [
{
Expand Down
9 changes: 5 additions & 4 deletions apps/portal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@trycompai/email": "workspace:*",
"@trycompai/kv": "workspace:*",
"@trycompai/ui": "workspace:*",
"@trycompai/ui-new": "workspace:*",
"@types/jszip": "^3.4.1",
"@upstash/ratelimit": "^2.0.5",
"archiver": "^7.0.1",
Expand Down Expand Up @@ -43,8 +44,8 @@
"typescript": "^5.8.3"
},
"peerDependencies": {
"react": "^19.1.1",
"react-dom": "^19.1.0"
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"private": true,
"scripts": {
Expand All @@ -53,9 +54,9 @@
"db:generate": "bun run db:getschema && prisma generate",
"db:getschema": "node ../../packages/db/scripts/combine-schemas.js && cp ../../packages/db/dist/schema.prisma prisma/schema.prisma",
"db:migrate": "cd ../../packages/db && bunx prisma migrate dev && cd ../../apps/portal",
"dev": "next dev --turbopack -p 3002",
"dev": "next dev -p 3002",
"lint": "next lint && prettier --check .",
"prebuild": "bun run db:generate",
"start": "next start"
}
}
}
14 changes: 8 additions & 6 deletions apps/portal/src/app/(app)/(home)/[orgId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ export default async function OrganizationPage({ params }: { params: Promise<{ o
const { orgId } = await params;

// Auth check with error handling
const session = await auth.api.getSession({
headers: await headers(),
}).catch((error) => {
console.error('Error getting session:', error);
redirect('/');
});
const session = await auth.api
.getSession({
headers: await headers(),
})
.catch((error) => {
console.error('Error getting session:', error);
redirect('/');
});

if (!session?.user) {
redirect('/auth');
Expand Down
2 changes: 1 addition & 1 deletion apps/portal/src/app/(app)/(home)/[orgId]/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,4 @@ export type MDM = {
enrollment_status: string;
name?: string;
server_url?: string;
};
};
3 changes: 0 additions & 3 deletions apps/portal/src/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ export default async function Layout({ children }: { children: React.ReactNode }
headers: await headers(),
});

console.log('session', session);

if (!session?.user) {
console.log('no session');
redirect('/auth');
}

Expand Down
13 changes: 10 additions & 3 deletions apps/portal/src/app/(public)/auth/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { LoginForm } from '@/app/components/login-form';
import { OtpSignIn } from '@/app/components/otp';
import { env } from '@/env.mjs';
import { Button } from '@comp/ui/button';
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@comp/ui/card';
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@comp/ui/card';
import { Icons } from '@comp/ui/icons';
import { Button } from '@trycompai/ui-new';
import { ArrowRight } from 'lucide-react';
import type { Metadata } from 'next';
import Link from 'next/link';
Expand Down Expand Up @@ -48,7 +55,7 @@ export default async function Page() {
Comp AI makes SOC 2, ISO 27001, HIPAA and GDPR effortless. Eliminate compliance
busywork, win more deals and accelerate growth.
</p>
<Button variant="link" className="mt-2 p-0" asChild>
<Button variant="outline" className="mt-2 p-0" asChild>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Button variant incompatible with zero padding styling

The Button changed from variant="link" (old UI library) to variant="outline" with className="mt-2 p-0". An outline variant button has a visible border, and p-0 removes all padding, causing the text content to touch the border directly. This creates a visually broken appearance. The equivalent variant in the new Chakra-based library for link-styled buttons is variant="plain", which has no border and works correctly with zero padding.

Fix in Cursor Fix in Web

<Link
href="https://trycomp.ai"
target="_blank"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client';

import {
Button,
HStack,
SUPPORTED_COLOR_PALETTES,
type SupportedColorPalette,
Text,
} from '@trycompai/ui-new';

export type ButtonColorPalette = SupportedColorPalette;

export function ButtonPaletteSwitcher({
value,
onChange,
}: {
value: ButtonColorPalette;
onChange: (value: ButtonColorPalette) => void;
}) {
return (
<HStack gap={3} align="center" flexWrap="wrap">
<Text color="fg" fontSize="sm" fontWeight="medium">
Palette
</Text>
<HStack
gap="2"
flexWrap="wrap"
bg="bg"
borderWidth="1px"
borderColor="border"
borderRadius="lg"
p="1"
boxShadow="xs"
role="group"
aria-label="Button palette"
>
{SUPPORTED_COLOR_PALETTES.map((palette) => {
const isSelected = palette === value;

return (
<Button
key={palette}
size="sm"
variant={isSelected ? 'solid' : 'outline'}
colorPalette={palette}
onClick={() => onChange(palette)}
aria-pressed={isSelected}
>
<HStack gap="2">
<Text as="span" textTransform="capitalize">
{palette}
</Text>
</HStack>
</Button>
);
})}
</HStack>
</HStack>
);
}
51 changes: 51 additions & 0 deletions apps/portal/src/app/(public)/test-ui/components/TestUiPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use client';

import {
Box,
ColorModeButton,
Container,
Flex,
Heading,
type SupportedColorPalette,
} from '@trycompai/ui-new';
import { useState } from 'react';
import { ButtonPaletteSwitcher } from './ButtonPaletteSwitcher';
import { TestUiSectionsBottom } from './TestUiSectionsBottom';
import { TestUiSectionsTop } from './TestUiSectionsTop';

export function TestUiPage() {
const [palette, setPalette] = useState<SupportedColorPalette>('primary');

return (
<Box pb="12">
<Box
position="sticky"
top="0"
zIndex="sticky"
bg="bg"
borderBottomWidth="1px"
borderBottomColor="border"
>
<Container maxW="1400px" py="4">
<Flex
align={{ base: 'stretch', md: 'center' }}
justify="space-between"
direction={{ base: 'column', md: 'row' }}
gap="4"
>
<Heading size="xl">Design System Preview</Heading>
<Flex align="center" justify="space-between" gap="4" flexWrap="wrap">
<ButtonPaletteSwitcher value={palette} onChange={setPalette} />
<ColorModeButton />
</Flex>
</Flex>
</Container>
</Box>

<Container maxW="1400px" py={8}>
<TestUiSectionsTop palette={palette} />
<TestUiSectionsBottom palette={palette} />
</Container>
</Box>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use client';

import { Box, Flex, Heading, HStack, Text } from '@trycompai/ui-new';
import type { ReactNode } from 'react';

export function Section({ title, children }: { title: string; children: ReactNode }) {
return (
<Box mb={12}>
<Heading size="lg" mb={6} pb={2} borderBottom="1px solid" borderColor="border">
{title}
</Heading>
{children}
</Box>
);
}

export function SubSection({ title, children }: { title: string; children: ReactNode }) {
return (
<Box mb={4}>
<Text fontWeight="semibold" mb={2}>
{title}
</Text>
{children}
</Box>
);
}

const DEFAULT_SHADES = [
'50',
'100',
'200',
'300',
'400',
'500',
'600',
'700',
'800',
'900',
'950',
] as const;

export function ColorRow({
name,
description,
shades = DEFAULT_SHADES,
highlightShade = '700',
}: {
name: string;
description?: string;
shades?: readonly string[];
highlightShade?: string;
}) {
return (
<Box w="full">
<HStack>
<Text fontWeight="semibold" fontSize="sm">
{name}
</Text>
{description && (
<Text fontSize="xs" color="gray.600">
{description}
</Text>
)}
</HStack>
{/* Add top padding (lg+) so shade labels above swatches don't overlap the header */}
<Flex gap={1} mt={2} pt={{ base: 0, lg: 6 }}>
{shades.map((shade) => (
<Box
key={shade}
bg={`${name}.${shade}`}
h={16}
flex={1}
borderRadius="sm"
title={`${name}.${shade}`}
position="relative"
outline={shade === highlightShade ? '2px solid' : undefined}
outlineColor={shade === highlightShade ? 'fg' : undefined}
outlineOffset={shade === highlightShade ? '2px' : undefined}
>
<Text
position="absolute"
bottom="calc(100% + 2px)"
left="50%"
transform="translateX(-50%)"
fontSize="xs"
color="gray.500"
display={{ base: 'none', lg: 'block' }}
>
{shade}
</Text>
</Box>
))}
</Flex>
</Box>
);
}
Loading