Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7a5e166
feat: add dub referral tab and embed
Marfuen Jun 24, 2025
48b07b1
Merge branch 'main' of github.com:trycompai/comp into mariano/comp-22…
Marfuen Jun 24, 2025
8112b5b
chore: add env vars to turbo json
Marfuen Jun 24, 2025
f9823f2
Merge pull request #1029 from trycompai/mariano/comp-229-dub-affiliates
Marfuen Jun 24, 2025
9119dd1
refactor: simplify onboarding process by removing localStorage depend…
claudfuen Jun 24, 2025
32cd92e
chore(ci): skip husky hooks in CI for semantic-release
claudfuen Jun 24, 2025
1455f33
Merge pull request #1031 from trycompai/claudio/comp-225-clean-up-ani…
claudfuen Jun 24, 2025
1a45ac4
feat(pricing): add '12-month minimum term' to paid features list in P…
claudfuen Jun 24, 2025
63a68b3
feat(pricing): add BookingDialog component to PricingCards for schedu…
claudfuen Jun 24, 2025
b39c654
fix(booking): remove unnecessary margin from Phone icon in BookingDia…
claudfuen Jun 24, 2025
2b66735
chore: add skeleton loader to be nicer
Marfuen Jun 24, 2025
a05ee82
Merge pull request #1033 from trycompai/mariano/comp-229-dub-affiliates
Marfuen Jun 24, 2025
161087f
refactor(booking): enhance BookingDialog layout and improve CalendarE…
claudfuen Jun 24, 2025
b240888
Merge pull request #1032 from trycompai/claudio/comp-225-clean-up-ani…
claudfuen Jun 24, 2025
33f5d73
chore: try system for theme
Marfuen Jun 24, 2025
e5f9851
chore: refactor to use server actions
Marfuen Jun 24, 2025
6e53571
chore: update env vars usage
Marfuen Jun 24, 2025
fb219f2
Merge branch 'main' into mariano/comp-229-dub-affiliates
Marfuen Jun 24, 2025
8e03d0a
Merge pull request #1034 from trycompai/mariano/comp-229-dub-affiliates
Marfuen Jun 24, 2025
4bd6018
feat(checkout): add CheckoutCompleteDialog component and integrate co…
claudfuen Jun 24, 2025
19fb0b9
chore: comment out referrals so claudio is unblocked
Marfuen Jun 24, 2025
68603f1
Merge branch 'mariano/comp-229-dub-affiliates' of github.com:trycompa…
Marfuen Jun 24, 2025
7ca7641
Merge pull request #1036 from trycompai/mariano/comp-229-dub-affiliates
Marfuen Jun 24, 2025
2182c9e
feat(checkout): enhance CheckoutCompleteDialog with updated features …
claudfuen Jun 24, 2025
3e1db8d
Merge branch 'main' of https://github.com/trycompai/comp into claudio…
claudfuen Jun 24, 2025
e91f1f0
fix(checkout): refactor CheckoutCompleteDialog to improve plan type h…
claudfuen Jun 24, 2025
bfbbcdb
Merge pull request #1035 from trycompai/claudio/comp-225-clean-up-ani…
claudfuen Jun 24, 2025
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
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # Uncomment if publishing to npm
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
HUSKY: 0 # Skip husky hooks in CI
run: npx semantic-release
4 changes: 4 additions & 0 deletions apps/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@dub/embed-react": "^0.0.14",
"@hookform/resolvers": "^5.1.1",
"@mendable/firecrawl-js": "^1.24.0",
"@nangohq/frontend": "^0.53.2",
Expand All @@ -38,14 +39,17 @@
"@tiptap/extension-table-row": "^2.22.3",
"@trigger.dev/react-hooks": "3.3.17",
"@trigger.dev/sdk": "3.3.17",
"@types/canvas-confetti": "^1.9.0",
"@types/three": "^0.177.0",
"@uploadthing/react": "^7.3.0",
"@upstash/ratelimit": "^2.0.5",
"@vercel/sdk": "^1.7.1",
"ai": "^4.3.16",
"axios": "^1.9.0",
"better-auth": "^1.2.8",
"canvas-confetti": "^1.9.3",
"d3": "^7.9.0",
"dub": "^0.63.5",
"framer-motion": "^12.18.1",
"geist": "^1.3.1",
"motion": "^12.9.2",
Expand Down
5 changes: 5 additions & 0 deletions apps/app/src/app/(app)/[orgId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getSubscriptionData } from '@/app/api/stripe/getSubscriptionData';
import { AnimatedLayout } from '@/components/animated-layout';
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';
Expand All @@ -10,6 +11,7 @@ import { db } from '@comp/db';
import dynamic from 'next/dynamic';
import { cookies, headers } from 'next/headers';
import { redirect } from 'next/navigation';
import { Suspense } from 'react';
import { OnboardingTracker } from './components/OnboardingTracker';

const HotKeys = dynamic(() => import('@/components/hot-keys').then((mod) => mod.HotKeys), {
Expand Down Expand Up @@ -100,6 +102,9 @@ export default async function Layout({
<SubscriptionProvider subscription={subscriptionData}>{children}</SubscriptionProvider>
</div>
<AssistantSheet />
<Suspense fallback={null}>
<CheckoutCompleteDialog />
</Suspense>
</AnimatedLayout>
<HotKeys />
</SidebarProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client';

import { DubEmbed } from '@dub/embed-react';

export const DubReferral = ({ publicToken }: { publicToken: string | null }) => {
if (!publicToken) {
return (
<div className="flex min-h-[400px] items-center justify-center">
<p className="text-sm text-muted-foreground">
No token available. Please try refreshing the page.
</p>
</div>
);
}

return (
<DubEmbed
data="referrals"
token={publicToken}
options={{
theme: 'system',
}}
/>
);
};
6 changes: 6 additions & 0 deletions apps/app/src/app/(app)/[orgId]/referrals/lib/dub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { env } from '@/env.mjs';
import { Dub } from 'dub';

export const dub = new Dub({
token: env.DUB_API_KEY,
});
36 changes: 36 additions & 0 deletions apps/app/src/app/(app)/[orgId]/referrals/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { env } from '@/env.mjs';
import { auth } from '@/utils/auth';
import { headers } from 'next/headers';
import { redirect } from 'next/navigation';
import { DubReferral } from './components/DubReferral';
import { dub } from './lib/dub';

export default async function ReferralsPage() {
const publicToken = await createPublicToken();

return <DubReferral publicToken={publicToken} />;
}

async function createPublicToken() {
const session = await auth.api.getSession({ headers: await headers() });

if (!session) {
redirect('/');
}

if (!env.DUB_PROGRAM_ID || !env.DUB_API_KEY) {
return null;
}

const { publicToken } = await dub.embedTokens.referrals({
programId: env.DUB_PROGRAM_ID,
partner: {
tenantId: session.user.id,
name: session.user.name || '',
email: session.user.email || '',
image: session.user.image || '',
},
});

return publicToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,6 @@ export function AnimatedGradientBackgroundWrapper() {
const [scale, setScale] = useState(0.7);

useEffect(() => {
// Function to calculate scale from localStorage
const updateScale = () => {
if (typeof window !== 'undefined') {
const stepIndex = parseInt(localStorage.getItem('onboarding-step-index') || '0');
const totalSteps = parseInt(localStorage.getItem('onboarding-total-steps') || '9');

// Calculate scale based on step progress (0.7 to 1.5)
const progressScale = 0.7 + (stepIndex / (totalSteps - 1)) * 0.8;
setScale(progressScale);
}
};

// Initial load
updateScale();

// Listen for step changes
const handleStepChange = (event: Event) => {
const customEvent = event as CustomEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,23 +62,18 @@ export function OrganizationSetupForm({

const hasExistingOrgs = existingOrganizations.length > 0;

// Save step progress to localStorage
// Dispatch custom event for background animation when step changes
useEffect(() => {
if (typeof window !== 'undefined') {
if (isFinalizing) {
// Set to max scale when finalizing
localStorage.setItem('onboarding-progress', '1');
window.dispatchEvent(
new CustomEvent('onboarding-step-change', {
detail: { stepIndex: steps.length - 1, totalSteps: steps.length, progress: 1 },
}),
);
} else {
const progress = stepIndex / (steps.length - 1);
localStorage.setItem('onboarding-step-index', stepIndex.toString());
localStorage.setItem('onboarding-total-steps', steps.length.toString());
localStorage.setItem('onboarding-progress', progress.toString());

// Dispatch custom event to notify the background wrapper
window.dispatchEvent(
new CustomEvent('onboarding-step-change', {
Expand Down
9 changes: 4 additions & 5 deletions apps/app/src/app/(app)/setup/hooks/useOnboardingForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import { z } from 'zod';
import { createOrganization } from '../actions/create-organization';
import { createOrganizationMinimal } from '../actions/create-organization-minimal';
import type { OnboardingFormFields } from '../components/OnboardingStepInput';
import { STORAGE_KEY, companyDetailsSchema, steps } from '../lib/constants';
import { companyDetailsSchema, steps } from '../lib/constants';
import { updateSetupSession } from '../lib/setup-session';
import type { CompanyDetails } from '../lib/types';
import { useLocalStorage } from './useLocalStorage';

interface UseOnboardingFormProps {
setupId?: string;
Expand All @@ -29,9 +28,8 @@ export function useOnboardingForm({
}: UseOnboardingFormProps = {}) {
const router = useRouter();

// If we have a setupId, use the initialData from KV, otherwise use localStorage
const [savedAnswers, setSavedAnswers] = useLocalStorage<Partial<CompanyDetails>>(
STORAGE_KEY,
// Use state instead of localStorage - initialized from KV data if setupId exists
const [savedAnswers, setSavedAnswers] = useState<Partial<CompanyDetails>>(
setupId && initialData ? initialData : {},
);

Expand Down Expand Up @@ -108,6 +106,7 @@ export function useOnboardingForm({
// Organization created, now redirect to plans page
router.push(`/upgrade/${data.organizationId}`);

// Clear answers after successful creation
setSavedAnswers({});
} else {
toast.error('Failed to create organization');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client';

import CalendarEmbed from '@/components/calendar-embed';
import { Button } from '@comp/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@comp/ui/dialog';
import { Phone } from 'lucide-react';

export function BookingDialog() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" size="sm" className="w-full">
<Phone className="h-4 w-4" />
Book a Call with Our Team
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-4xl md:max-w-5xl lg:max-w-6xl w-[95vw] h-[70vh] p-0 overflow-y-auto">
<div className="flex flex-col h-full">
<DialogHeader className="px-8 py-6 border-b">
<DialogTitle className="text-xl font-semibold">
Schedule a Call with Our Compliance Experts
</DialogTitle>
<DialogDescription className="mt-2">
Have questions about our plans? Want to learn how we can help you achieve compliance
in 14 days? Book a call with our team.
</DialogDescription>
</DialogHeader>
<div className="flex-1 mx-auto w-full">
<CalendarEmbed />
</div>
</div>
</DialogContent>
</Dialog>
);
}
16 changes: 14 additions & 2 deletions apps/app/src/app/(app)/upgrade/[orgId]/pricing-cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useAction } from 'next-safe-action/hooks';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { toast } from 'sonner';
import { BookingDialog } from './components/BookingDialog';

interface PricingCardsProps {
organizationId: string;
Expand Down Expand Up @@ -196,6 +197,7 @@ const paidFeatures = [
'Dedicated Success Team',
'24x7x365 Support & SLA',
'Slack Channel with Comp AI',
'12-month minimum term',
];

export function PricingCards({ organizationId, priceDetails }: PricingCardsProps) {
Expand All @@ -218,7 +220,7 @@ export function PricingCards({ organizationId, priceDetails }: PricingCardsProps
chooseSelfServeAction,
{
onSuccess: () => {
router.push(`/${organizationId}`);
router.push(`/${organizationId}?checkoutComplete=starter`);
},
onError: ({ error }) => {
toast.error(error.serverError || 'Failed to set up free plan');
Expand Down Expand Up @@ -250,7 +252,7 @@ export function PricingCards({ organizationId, priceDetails }: PricingCardsProps
organizationId,
mode: 'subscription',
priceId,
successUrl: `${baseUrl}/${organizationId}/settings/billing?success=true`,
successUrl: `${baseUrl}/api/stripe/success?organizationId=${organizationId}&planType=done-for-you`,
cancelUrl: `${baseUrl}/upgrade/${organizationId}`,
allowPromotionCodes: true,
metadata: {
Expand Down Expand Up @@ -446,6 +448,16 @@ export function PricingCards({ organizationId, priceDetails }: PricingCardsProps
<ReviewSection rating={4.7} reviewCount={100} />
</div>
</Card>

{/* Help Section */}
<Card className="bg-muted/30 border-dashed">
<CardContent className="p-4">
<p className="text-xs text-muted-foreground text-center mb-3">
Have questions? We're here to help
</p>
<BookingDialog />
</CardContent>
</Card>
</div>
</div>
</div>
Expand Down
8 changes: 5 additions & 3 deletions apps/app/src/app/api/stripe/success/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { syncStripeDataToKV } from '../syncStripeDataToKv';
export async function GET(req: Request) {
const { user } = await getServersideSession(req);

// Extract organizationId from query parameters
// Extract organizationId and planType from query parameters
const url = new URL(req.url);
const organizationId = url.searchParams.get('organizationId');
const planType = url.searchParams.get('planType') || 'done-for-you'; // Default to done-for-you for backwards compatibility

if (!organizationId) {
return redirect('/');
Expand All @@ -29,9 +30,10 @@ export async function GET(req: Request) {

const stripeCustomerId = await client.get(`stripe:organization:${organizationId}`);
if (!stripeCustomerId) {
return redirect('/');
return redirect(`/${organizationId}`);
}

await syncStripeDataToKV(stripeCustomerId as string);
return redirect('/');
// Redirect with the plan type from query parameters
return redirect(`/${organizationId}/frameworks?checkoutComplete=${planType}`);
}
7 changes: 1 addition & 6 deletions apps/app/src/components/calendar-embed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ export default function CalendarEmbed() {
}, []);

return (
<Cal
namespace="meet-us"
calLink="team/compai/meet-us"
style={{ width: '100%', height: '100%', overflow: 'scroll' }}
config={{ layout: 'month_view' }}
/>
<Cal namespace="meet-us" calLink="team/compai/meet-us" config={{ layout: 'month_view' }} />
);
}
Loading
Loading