Skip to content

Commit f4a5917

Browse files
authored
Merge pull request #1027 from trycompai/main
[comp] Production Deploy
2 parents ed1f301 + ec6fbb3 commit f4a5917

Some content is hidden

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

50 files changed

+1001
-230
lines changed

apps/app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"puppeteer-core": "^24.7.2",
6060
"react-email": "^4.0.15",
6161
"react-hook-form": "^7.58.1",
62-
"react-hotkeys-hook": "^4.6.2",
62+
"react-hotkeys-hook": "^5.1.0",
6363
"react-intersection-observer": "^9.16.0",
6464
"react-markdown": "^9.1.0",
6565
"react-textarea-autosize": "^8.5.9",

apps/app/src/app/(app)/no-access/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default async function NoAccess() {
3131
});
3232

3333
return (
34-
<div className="bg-foreground/05 flex h-screen flex-col items-center justify-center gap-4">
34+
<div className="bg-foreground/05 flex h-dvh flex-col items-center justify-center gap-4">
3535
<h1 className="text-2xl font-bold">Access Denied</h1>
3636
<div className="flex flex-col text-center">
3737
<p>

apps/app/src/app/(app)/setup/components/AnimatedGradientBackground.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,14 @@ function AnimatedOrb({ scale = 1 }: AnimatedOrbProps) {
300300
// Handle ESC key for rage mode easter egg
301301
useEffect(() => {
302302
const handleKeyDown = (e: KeyboardEvent) => {
303+
// Don't process keyboard events if user is typing in an input
304+
const target = e.target as HTMLElement;
305+
const isInputElement = target.matches('input, textarea, select, [contenteditable="true"]');
306+
307+
if (isInputElement) {
308+
return; // Skip processing for form inputs
309+
}
310+
303311
if (e.key === 'Escape') {
304312
e.preventDefault();
305313
rageMode.current = !rageMode.current;

apps/app/src/app/(app)/setup/components/OnboardingStepInput.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
33
import { SelectPills } from '@comp/ui/select-pills';
44
import { Textarea } from '@comp/ui/textarea';
55
import type { UseFormReturn } from 'react-hook-form';
6+
import { Controller } from 'react-hook-form';
67
import type { CompanyDetails, Step } from '../lib/types';
78
import { FrameworkSelection } from './FrameworkSelection';
9+
import { WebsiteInput } from './WebsiteInput';
810

911
// Type for form fields used in this component.
1012
// For now, defining it here to match OrganizationSetupForm.tsx structure.
@@ -35,6 +37,16 @@ export function OnboardingStepInput({
3537
);
3638
}
3739

40+
if (currentStep.key === 'website') {
41+
return (
42+
<Controller
43+
name={currentStep.key}
44+
control={form.control}
45+
render={({ field }) => <WebsiteInput {...field} placeholder="example.com" autoFocus />}
46+
/>
47+
);
48+
}
49+
3850
if (currentStep.key === 'describe') {
3951
return (
4052
<Textarea
@@ -81,7 +93,7 @@ export function OnboardingStepInput({
8193
onValueChange={(values: string[]) => {
8294
form.setValue(currentStep.key, values.join(','));
8395
}}
84-
placeholder={`Type anything and press enter to add it, ${currentStep.placeholder}`}
96+
placeholder={`Search or add custom (press Enter) • ${currentStep.placeholder}`}
8597
/>
8698
);
8799
}

apps/app/src/app/(app)/setup/components/OrganizationSetupForm.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export function OrganizationSetupForm({
9090
}, [stepIndex, steps.length, isFinalizing]);
9191

9292
return isFinalizing ? (
93-
<div className="flex min-h-screen items-center justify-center">
93+
<div className="flex min-h-dvh items-center justify-center">
9494
<LogoSpinner />
9595
</div>
9696
) : (
@@ -157,9 +157,9 @@ export function OrganizationSetupForm({
157157
</div>
158158
<div className="w-full border-t border-border/30 pt-3">
159159
<p className="text-center text-xs text-muted-foreground/70">
160-
<span className="inline-flex items-center gap-1">
160+
<span className="inline-flex items-center justify-center gap-1.5 flex-wrap">
161161
<svg
162-
className="h-3 w-3"
162+
className="h-3.5 w-3.5 flex-shrink-0"
163163
fill="none"
164164
stroke="currentColor"
165165
viewBox="0 0 24 24"
@@ -168,11 +168,13 @@ export function OrganizationSetupForm({
168168
<path
169169
strokeLinecap="round"
170170
strokeLinejoin="round"
171-
strokeWidth={2}
172-
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
171+
strokeWidth={1.5}
172+
d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 001.423 1.423l1.183.394-1.183.394a2.25 2.25 0 00-1.423 1.423z"
173173
/>
174174
</svg>
175-
Your answers will be used by our AI to create a personalized compliance plan
175+
<span className="max-w-[280px] sm:max-w-none">
176+
AI personalizes your plan based on your answers
177+
</span>
176178
</span>
177179
</p>
178180
</div>
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
'use client';
2+
3+
import { Input } from '@comp/ui/input';
4+
import { type InputHTMLAttributes, forwardRef, useCallback, useEffect, useState } from 'react';
5+
6+
interface WebsiteInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'prefix'> {
7+
onValueChange?: (value: string) => void;
8+
}
9+
10+
export const WebsiteInput = forwardRef<HTMLInputElement, WebsiteInputProps>(
11+
({ value: propValue, onChange, onValueChange, onBlur, ...props }, ref) => {
12+
// Use local state for the display value
13+
const [displayValue, setDisplayValue] = useState('');
14+
15+
// Update display value when prop value changes
16+
useEffect(() => {
17+
if (typeof propValue === 'string') {
18+
setDisplayValue(propValue.replace(/^https?:\/\//, '').replace(/^www\./, ''));
19+
}
20+
}, [propValue]);
21+
22+
const handleChange = useCallback(
23+
(e: React.ChangeEvent<HTMLInputElement>) => {
24+
let inputValue = e.target.value;
25+
26+
// Clean up the input
27+
inputValue = inputValue.trim();
28+
29+
// Remove any protocol if pasted
30+
inputValue = inputValue.replace(/^https?:\/\//, '');
31+
inputValue = inputValue.replace(/^ftp:\/\//, '');
32+
inputValue = inputValue.replace(/^\/\//, '');
33+
34+
// Clean up multiple slashes (except for the protocol)
35+
inputValue = inputValue.replace(/([^:]\/)\/+/g, '$1');
36+
37+
// Update display value
38+
setDisplayValue(inputValue);
39+
40+
// If empty, pass empty value
41+
if (!inputValue) {
42+
onChange?.({
43+
...e,
44+
target: { ...e.target, value: '', name: e.target.name },
45+
} as React.ChangeEvent<HTMLInputElement>);
46+
onValueChange?.('');
47+
return;
48+
}
49+
50+
// Format the value with https://
51+
const finalValue = `https://${inputValue}`;
52+
53+
// Create a synthetic event with the formatted value
54+
const syntheticEvent = {
55+
...e,
56+
target: {
57+
...e.target,
58+
value: finalValue,
59+
name: e.target.name,
60+
},
61+
} as React.ChangeEvent<HTMLInputElement>;
62+
63+
// Call the original onChange if provided
64+
onChange?.(syntheticEvent);
65+
66+
// Also call onValueChange if provided
67+
onValueChange?.(finalValue);
68+
},
69+
[onChange, onValueChange],
70+
);
71+
72+
const handleBlur = useCallback(
73+
(e: React.FocusEvent<HTMLInputElement>) => {
74+
// On blur, ensure the value has a proper format
75+
if (displayValue && !displayValue.includes('.')) {
76+
// If there's no dot, add .com as a helpful default
77+
const finalValue = `https://${displayValue}.com`;
78+
const syntheticEvent = {
79+
...e,
80+
target: {
81+
...e.target,
82+
value: finalValue,
83+
name: e.target.name,
84+
},
85+
} as React.FocusEvent<HTMLInputElement>;
86+
87+
setDisplayValue(`${displayValue}.com`);
88+
onChange?.({ ...syntheticEvent, type: 'change' } as React.ChangeEvent<HTMLInputElement>);
89+
onValueChange?.(finalValue);
90+
}
91+
92+
// Call original onBlur
93+
onBlur?.(e);
94+
},
95+
[displayValue, onChange, onValueChange, onBlur],
96+
);
97+
98+
return (
99+
<Input
100+
ref={ref}
101+
type="text"
102+
value={displayValue}
103+
onChange={handleChange}
104+
onBlur={handleBlur}
105+
prefix="https://"
106+
autoComplete="url"
107+
spellCheck={false}
108+
{...props}
109+
/>
110+
);
111+
},
112+
);
113+
114+
WebsiteInput.displayName = 'WebsiteInput';

apps/app/src/app/(app)/setup/go/[id]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default async function RunPage({ params }: PageProps) {
1414

1515
return (
1616
<TriggerProvider accessToken={publicAccessToken?.value ?? ''}>
17-
<div className="bg-background flex min-h-screen items-center justify-center p-6 md:p-8">
17+
<div className="bg-background flex min-h-dvh items-center justify-center p-6 md:p-8">
1818
<div className="bg-card relative w-full max-w-[440px] border p-8 shadow-lg">
1919
<div className="animate-in fade-in slide-in-from-bottom-4 flex flex-col justify-center space-y-4 duration-300">
2020
<div className="flex flex-col justify-center gap-2">

apps/app/src/app/(app)/setup/hooks/useOnboardingForm.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ export function useOnboardingForm({
8282
setIsFinalizing(true);
8383
sendGTMEvent({ event: 'conversion' });
8484

85-
// Organization created, now redirect to loading step
86-
router.push(`/setup/loading/${data.organizationId}`);
85+
// Organization created, now redirect to plans page
86+
router.push(`/upgrade/${data.organizationId}`);
8787
} else {
8888
toast.error(data?.error || 'Failed to create organization minimal');
8989
setIsSkipping(false);
@@ -105,8 +105,8 @@ export function useOnboardingForm({
105105
setIsFinalizing(true);
106106
sendGTMEvent({ event: 'conversion' });
107107

108-
// Organization created, now redirect to loading step
109-
router.push(`/setup/loading/${data.organizationId}`);
108+
// Organization created, now redirect to plans page
109+
router.push(`/upgrade/${data.organizationId}`);
110110

111111
setSavedAnswers({});
112112
} else {

apps/app/src/app/(app)/setup/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default async function SetupLayout({ children }: { children: React.ReactN
1818
const currentOrganization = null; // No current org in setup
1919

2020
return (
21-
<main className="flex min-h-screen flex-col">
21+
<main className="flex min-h-dvh flex-col">
2222
<AnimatedGradientBackgroundWrapper />
2323
<MinimalHeader
2424
user={session.user}

apps/app/src/app/(app)/setup/lib/constants.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const steps: Step[] = [
3535
{
3636
key: 'website',
3737
question: "What's your company website?",
38-
placeholder: 'e.g., https://www.acme.com',
38+
placeholder: 'example.com',
3939
},
4040
{
4141
key: 'describe',
@@ -100,7 +100,7 @@ export const steps: Step[] = [
100100
key: 'workLocation',
101101
question: 'How does your team work?',
102102
placeholder: 'e.g., Remote',
103-
options: ['Fully remote', 'Hybrid (office + remote)', 'Office-based', 'Other'],
103+
options: ['Fully remote', 'Hybrid (office + remote)', 'Office-based'],
104104
},
105105
{
106106
key: 'infrastructure',

0 commit comments

Comments
 (0)