Skip to content

Commit 1da2c91

Browse files
committed
feat(portal): migrate to @trycompai/ui-shadcn and update UI components
1 parent c8dc4a0 commit 1da2c91

File tree

96 files changed

+2947
-3489
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+2947
-3489
lines changed

apps/portal/agents.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
# Portal AGENTS.md
22

3-
## Component usage
3+
## UI rules (hard)
44

5-
- Only use components from the `packages/ui-v2` package.
6-
- Import components from `@trycompai/ui-v2` (or the appropriate package name).
7-
- Do not use components from `packages/ui` or other UI packages.
5+
- **Only use existing components from `@trycompai/ui-shadcn`.**
6+
- **Do not add new UI components** (no new primitives/wrappers in the Portal app).
7+
- **Do not add new components to `packages/ui-shadcn`** as part of Portal work.
8+
- If a needed UI component doesn't exist in `@trycompai/ui-shadcn`, **stop and ask** instead of building an ad-hoc replacement.
9+
10+
## Forbidden UI packages
11+
12+
- `@comp/ui`
13+
- `@trycompai/ui`
14+
- `@trycompai/ui-v2`

apps/portal/next.config.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import './src/env.mjs';
44
const isStandalone = process.env.NEXT_OUTPUT_STANDALONE === 'true';
55

66
const config = {
7-
transpilePackages: ['@trycompai/db', '@trycompai/ui-v2'],
8-
serverExternalPackages: ['@ark-ui/react', '@zag-js/anatomy'],
9-
experimental: {
10-
optimizePackageImports: ['@chakra-ui/react'],
11-
},
7+
transpilePackages: ['@trycompai/db', '@trycompai/ui-shadcn'],
128
images: {
139
remotePatterns: [
1410
{

apps/portal/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@trycompai/db": "1.3.19",
1414
"@trycompai/email": "workspace:*",
1515
"@trycompai/kv": "workspace:*",
16-
"@trycompai/ui-v2": "workspace:*",
16+
"@trycompai/ui-shadcn": "workspace:*",
1717
"@types/jszip": "^3.4.1",
1818
"@upstash/ratelimit": "^2.0.5",
1919
"archiver": "^7.0.1",

apps/portal/postcss.config.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const config = {
2+
plugins: {
3+
'@tailwindcss/postcss': {},
4+
},
5+
};
6+
7+
export default config;

apps/portal/src/app/(app)/(home)/[orgId]/components/EmployeeTasksList.tsx

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use client';
22

33
import { trainingVideos } from '@/lib/data/training-videos';
4-
import { Accordion, Card, Progress, Text, VStack } from '@trycompai/ui-v2';
5-
import { useState } from 'react';
4+
import { Card, CardContent, CardHeader } from '@trycompai/ui-shadcn';
65
import type { EmployeePortalDashboard } from '../types/employee-portal';
76
import { DeviceAgentAccordionItem } from './tasks/DeviceAgentAccordionItem';
87
import { GeneralTrainingAccordionItem } from './tasks/GeneralTrainingAccordionItem';
@@ -23,8 +22,6 @@ export const EmployeeTasksList = ({
2322
fleetPolicies,
2423
host,
2524
}: EmployeeTasksListProps) => {
26-
const [accordionValue, setAccordionValue] = useState<string[]>([]);
27-
2825
// Check completion status
2926
const hasAcceptedPolicies =
3027
policies.length === 0 || policies.every((p) => p.signedBy.includes(member.id));
@@ -56,41 +53,39 @@ export const EmployeeTasksList = ({
5653
const progressPercent = (completedCount / totalCount) * 100;
5754

5855
return (
59-
<VStack align="stretch" gap="4">
60-
<Card.Root>
61-
<Card.Header>
62-
<Card.Title>Overview</Card.Title>
63-
<Card.Description>
64-
Please complete the following tasks to stay compliant and secure.
65-
</Card.Description>
66-
</Card.Header>
56+
<div className="flex flex-col gap-4">
57+
<Card>
58+
<CardHeader>
59+
<div className="flex flex-col gap-1">
60+
<h2 className="text-base font-semibold">Overview</h2>
61+
<p className="text-sm text-muted-foreground">
62+
Please complete the following tasks to stay compliant and secure.
63+
</p>
64+
</div>
65+
</CardHeader>
6766

68-
<Card.Body>
69-
<VStack align="stretch" gap="4">
70-
<VStack align="stretch" gap="2">
71-
<Text fontSize="sm" color="fg.muted">
67+
<CardContent>
68+
<div className="flex flex-col gap-4">
69+
<div className="flex flex-col gap-2">
70+
<p className="text-sm text-muted-foreground">
7271
{completedCount} of {totalCount} tasks completed
73-
</Text>
74-
<Progress.Root value={progressPercent} colorPalette="primary">
75-
<Progress.Track>
76-
<Progress.Range />
77-
</Progress.Track>
78-
</Progress.Root>
79-
</VStack>
72+
</p>
73+
<div className="h-2 w-full rounded-full bg-muted">
74+
<div
75+
className="h-2 rounded-full bg-primary transition-[width]"
76+
style={{ width: `${progressPercent}%` }}
77+
/>
78+
</div>
79+
</div>
8080

81-
<Accordion.Root
82-
multiple={false}
83-
collapsible
84-
value={accordionValue}
85-
onValueChange={({ value }) => setAccordionValue(value)}
86-
>
81+
<div className="divide-y divide-border rounded-md border border-border">
8782
<PoliciesAccordionItem policies={policies} member={member} />
8883
<DeviceAgentAccordionItem member={member} host={host} fleetPolicies={fleetPolicies} />
8984
<GeneralTrainingAccordionItem trainingVideoCompletions={trainingVideoCompletions} />
90-
</Accordion.Root>
91-
</VStack>
92-
</Card.Body>
93-
</Card.Root>
94-
</VStack>
85+
</div>
86+
</div>
87+
</CardContent>
88+
</Card>
89+
</div>
9590
);
9691
};

apps/portal/src/app/(app)/(home)/[orgId]/components/policy/AdvancedEditor.tsx

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { Editor, type JSONContent } from '@comp/ui/editor';
3+
import type { JSONContent } from '@tiptap/react';
44

55
interface AdvancedEditorProps {
66
initialContent?: JSONContent | JSONContent[];
@@ -9,19 +9,43 @@ interface AdvancedEditorProps {
99
saveDebounceMs?: number;
1010
}
1111

12+
const getPlainText = (node: JSONContent): string => {
13+
if (!node) return '';
14+
15+
if (node.type === 'text') {
16+
return node.text ?? '';
17+
}
18+
19+
if (Array.isArray(node.content)) {
20+
return node.content.map((child) => getPlainText(child)).join('');
21+
}
22+
23+
return '';
24+
};
25+
1226
const AdvancedEditor = ({ initialContent }: AdvancedEditorProps) => {
1327
if (!initialContent) return null;
1428

29+
const doc: JSONContent = Array.isArray(initialContent)
30+
? { type: 'doc', content: initialContent }
31+
: initialContent;
32+
33+
const blocks = Array.isArray(doc.content) ? doc.content : [];
34+
1535
return (
16-
<Editor
17-
initialContent={initialContent}
18-
readOnly={true}
19-
showSaveStatus={false}
20-
showWordCount={false}
21-
showToolbar={false}
22-
minHeight="500px"
23-
className="max-w-screen-lg mx-auto sm:mb-[calc(20vh)]"
24-
/>
36+
<div className="mx-auto max-w-screen-lg sm:mb-[calc(20vh)]">
37+
<div className="space-y-3 text-sm leading-6">
38+
{blocks.map((block, index) => {
39+
const text = getPlainText(block).trim();
40+
if (!text) return null;
41+
return (
42+
<p key={index} className="whitespace-pre-wrap">
43+
{text}
44+
</p>
45+
);
46+
})}
47+
</div>
48+
</div>
2549
);
2650
};
2751

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
'use client';
22

3-
import { Button } from '@comp/ui/button';
4-
import {
5-
Card,
6-
CardContent,
7-
CardDescription,
8-
CardFooter,
9-
CardHeader,
10-
CardTitle,
11-
} from '@comp/ui/card';
123
import type { Member, Policy } from '@db';
134
import type { JSONContent } from '@tiptap/react';
5+
import { Button, Card, CardContent, CardFooter, CardHeader } from '@trycompai/ui-shadcn';
146
import { ArrowRight, Check } from 'lucide-react';
157
import { useState } from 'react';
168
import { PolicyEditor } from './PolicyEditor';
@@ -36,60 +28,68 @@ export function PolicyCard({ policy, onNext, onComplete, member, isLastPolicy }:
3628
const isPdfPolicy = policy.displayFormat === 'PDF';
3729

3830
return (
39-
<Card className="relative flex max-h-[calc(100vh-450px)] w-full flex-col shadow-md">
40-
{isAccepted && (
41-
<div className="bg-background/80 absolute inset-0 z-10 flex items-center justify-center backdrop-blur-xs">
42-
<div className="space-y-4 text-center">
43-
<Check className="text-primary mx-auto h-12 w-12" />
44-
<h3 className="text-xl font-semibold">Policy Accepted</h3>
45-
<p className="text-muted-foreground">You have accepted this policy</p>
46-
<div className="flex justify-center gap-2">
47-
<Button variant="outline" onClick={() => setIsAccepted(false)} className="gap-2">
48-
View Again
49-
</Button>
50-
{!isLastPolicy && (
51-
<Button onClick={onNext} className="gap-2">
52-
Next Policy
53-
<ArrowRight className="h-4 w-4" />
31+
<div className="relative max-h-[calc(100vh-450px)] w-full">
32+
<Card>
33+
{isAccepted && (
34+
<div className="bg-background/80 absolute inset-0 z-10 flex items-center justify-center backdrop-blur-xs">
35+
<div className="space-y-4 text-center">
36+
<Check className="text-primary mx-auto h-12 w-12" />
37+
<h3 className="text-xl font-semibold">Policy Accepted</h3>
38+
<p className="text-muted-foreground">You have accepted this policy</p>
39+
<div className="flex justify-center gap-2">
40+
<Button variant="outline" onClick={() => setIsAccepted(false)}>
41+
View Again
5442
</Button>
55-
)}
43+
{!isLastPolicy && (
44+
<Button onClick={onNext}>
45+
<span className="inline-flex items-center gap-2">
46+
Next Policy
47+
<ArrowRight className="h-4 w-4" />
48+
</span>
49+
</Button>
50+
)}
51+
</div>
5652
</div>
5753
</div>
58-
</div>
59-
)}
60-
<CardHeader>
61-
<CardTitle className="text-2xl">{policy.name}</CardTitle>
62-
<CardDescription className="text-muted-foreground">{policy.description}</CardDescription>
63-
</CardHeader>
64-
<CardContent className="w-full flex-1 overflow-y-auto">
65-
<div className="w-full border-t pt-6">
66-
<div className="max-w-none">
67-
{isPdfPolicy ? (
68-
<PortalPdfViewer policyId={policy.id} s3Key={policy.pdfUrl} />
69-
) : (
70-
<PolicyEditor content={policy.content as JSONContent[]} />
71-
)}
54+
)}
55+
<CardHeader>
56+
<div className="flex flex-col gap-1">
57+
<h2 className="text-xl font-semibold">{policy.name}</h2>
58+
{policy.description ? (
59+
<p className="text-sm text-muted-foreground">{policy.description}</p>
60+
) : null}
61+
</div>
62+
</CardHeader>
63+
<CardContent>
64+
<div className="w-full border-t pt-6">
65+
<div className="max-w-none">
66+
{isPdfPolicy ? (
67+
<PortalPdfViewer policyId={policy.id} s3Key={policy.pdfUrl} />
68+
) : (
69+
<PolicyEditor content={policy.content as JSONContent[]} />
70+
)}
71+
</div>
72+
<div className="mt-4 text-sm text-muted-foreground">
73+
<span>Status: {policy.status}</span>
74+
{policy.updatedAt ? (
75+
<span> (Last updated: {new Date(policy.updatedAt).toLocaleDateString()})</span>
76+
) : null}
77+
</div>
7278
</div>
73-
<p className="text-muted-foreground mt-4 text-sm">
74-
Status: {policy.status}{' '}
79+
</CardContent>
80+
<CardFooter>
81+
<div className="flex items-center gap-2">
7582
{policy.updatedAt && (
76-
<span>(Last updated: {new Date(policy.updatedAt).toLocaleDateString()})</span>
83+
<p className="text-muted-foreground text-sm">
84+
Last updated: {new Date(policy.updatedAt).toLocaleDateString()}
85+
</p>
7786
)}
78-
</p>
79-
</div>
80-
</CardContent>
81-
<CardFooter className="flex items-center justify-between">
82-
<div className="flex items-center gap-2">
83-
{policy.updatedAt && (
84-
<p className="text-muted-foreground text-sm">
85-
Last updated: {new Date(policy.updatedAt).toLocaleDateString()}
86-
</p>
87-
)}
88-
</div>
89-
<div className="flex gap-2">
90-
<Button onClick={handleAccept}>Accept Policy</Button>
91-
</div>
92-
</CardFooter>
93-
</Card>
87+
</div>
88+
<div className="flex gap-2">
89+
<Button onClick={handleAccept}>Accept Policy</Button>
90+
</div>
91+
</CardFooter>
92+
</Card>
93+
</div>
9494
);
9595
}

apps/portal/src/app/(app)/(home)/[orgId]/components/policy/PolicyCarousel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

3-
import { Button } from '@comp/ui/button';
43
import type { Member, Policy } from '@db';
4+
import { Button } from '@trycompai/ui-shadcn';
55
import { ChevronLeft, ChevronRight } from 'lucide-react';
66
import { useAction } from 'next-safe-action/hooks';
77
import { useEffect, useRef, useState } from 'react';

apps/portal/src/app/(app)/(home)/[orgId]/components/policy/PolicyContainer.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

3-
import { Button } from '@comp/ui/button';
43
import type { Member, Policy } from '@db';
4+
import { Button } from '@trycompai/ui-shadcn';
55
import { ArrowLeft } from 'lucide-react';
66
import { useState } from 'react';
77
import { PolicyCarousel } from './PolicyCarousel';
@@ -31,9 +31,11 @@ export function PolicyContainer({ policies, member }: PolicyContainerProps) {
3131
return (
3232
<div className="space-y-4">
3333
<div className="flex items-center gap-4">
34-
<Button variant="outline" size="sm" className="gap-2" onClick={handleBackToGrid}>
35-
<ArrowLeft className="h-4 w-4" />
36-
Back to Policies
34+
<Button variant="outline" size="sm" onClick={handleBackToGrid}>
35+
<span className="inline-flex items-center gap-2">
36+
<ArrowLeft className="h-4 w-4" />
37+
Back to Policies
38+
</span>
3739
</Button>
3840
<p className="text-muted-foreground text-sm">
3941
Policy {selectedPolicyIndex + 1} of {policies.length}

0 commit comments

Comments
 (0)