diff --git a/.docker/scripts/postgres/create-databases.sh b/.docker/scripts/postgres/create-databases.sh
index 481352f2c3..347f026f8a 100755
--- a/.docker/scripts/postgres/create-databases.sh
+++ b/.docker/scripts/postgres/create-databases.sh
@@ -6,6 +6,6 @@ set -u
if [ -n "$POSTGRES_DATABASES" ]; then
for db in $(echo $POSTGRES_DATABASES | tr ',' ' '); do
echo "Creating $db..."
- psql -v ON_ERROR_STOP=1 -c "CREATE DATABASE $db;"
+ psql -U postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE $db;"
done
fi
diff --git a/.env b/.env
index 8e115d0fe5..bedfcf9eea 100644
--- a/.env
+++ b/.env
@@ -1,6 +1,7 @@
BILLING_PLAN_ENV=dev
VSCODE_EXTENSION_BASE_URL=vscode://RooVeterinaryInc.roo-cline
+CURSOR_EXTENSION_BASE_URL=cursor://RooVeterinaryInc.roo-cline
# Clerk
# https://clerk.com/docs/deployments/clerk-environment-variables#sign-in-and-sign-up-redirects
diff --git a/.storybook/preview.ts b/.storybook/preview.ts
index 0e436a643c..211803d989 100644
--- a/.storybook/preview.ts
+++ b/.storybook/preview.ts
@@ -1,4 +1,4 @@
-import '../src/styles/global.css';
+import '../src/styles/globals.css';
import type { Preview } from '@storybook/react';
diff --git a/eslint.config.mjs b/eslint.config.mjs
index fd78887650..ae1e4d0403 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -28,7 +28,7 @@ export default [
},
},
{
- ignores: ['dist/**', '.next'],
+ ignores: ['dist/**', '.next', 'storybook-static'],
},
{
...pluginReact.configs.flat.recommended,
diff --git a/migrations/0000_hesitant_kylun.sql b/migrations/0000_hesitant_kylun.sql
new file mode 100644
index 0000000000..2c76dcc0fd
--- /dev/null
+++ b/migrations/0000_hesitant_kylun.sql
@@ -0,0 +1,8 @@
+CREATE TABLE "events" (
+ "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "events_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
+ "type" text NOT NULL,
+ "timestamp" bigint NOT NULL,
+ "properties" json NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
diff --git a/migrations/0000_misty_leo.sql b/migrations/0000_misty_leo.sql
deleted file mode 100644
index 8854258552..0000000000
--- a/migrations/0000_misty_leo.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-CREATE TABLE "organization" (
- "id" text PRIMARY KEY NOT NULL,
- "stripe_customer_id" text,
- "stripe_subscription_id" text,
- "stripe_subscription_price_id" text,
- "stripe_subscription_status" text,
- "stripe_subscription_current_period_end" bigint,
- "updated_at" timestamp DEFAULT now() NOT NULL,
- "created_at" timestamp DEFAULT now() NOT NULL
-);
---> statement-breakpoint
-CREATE UNIQUE INDEX "stripe_customer_id_idx" ON "organization" USING btree ("stripe_customer_id");
\ No newline at end of file
diff --git a/migrations/meta/0000_snapshot.json b/migrations/meta/0000_snapshot.json
index 8d2c1bea6f..4ebacc9d10 100644
--- a/migrations/meta/0000_snapshot.json
+++ b/migrations/meta/0000_snapshot.json
@@ -1,55 +1,47 @@
{
- "id": "013accf9-070a-47cd-9b49-1318a1c8a237",
+ "id": "5ab333f5-a980-4564-a919-3d15608505d0",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
- "public.organization": {
- "name": "organization",
+ "public.events": {
+ "name": "events",
"schema": "",
"columns": {
"id": {
"name": "id",
- "type": "text",
+ "type": "integer",
"primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "events_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
"notNull": true
},
- "stripe_customer_id": {
- "name": "stripe_customer_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "stripe_subscription_id": {
- "name": "stripe_subscription_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "stripe_subscription_price_id": {
- "name": "stripe_subscription_price_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "stripe_subscription_status": {
- "name": "stripe_subscription_status",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "stripe_subscription_current_period_end": {
- "name": "stripe_subscription_current_period_end",
+ "timestamp": {
+ "name": "timestamp",
"type": "bigint",
"primaryKey": false,
- "notNull": false
+ "notNull": true
},
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp",
+ "properties": {
+ "name": "properties",
+ "type": "json",
"primaryKey": false,
- "notNull": true,
- "default": "now()"
+ "notNull": true
},
"created_at": {
"name": "created_at",
@@ -57,25 +49,16 @@
"primaryKey": false,
"notNull": true,
"default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
}
},
- "indexes": {
- "stripe_customer_id_idx": {
- "name": "stripe_customer_id_idx",
- "columns": [
- {
- "expression": "stripe_customer_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": true,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
+ "indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json
index c6f820ea20..11751f2659 100644
--- a/migrations/meta/_journal.json
+++ b/migrations/meta/_journal.json
@@ -5,8 +5,8 @@
{
"idx": 0,
"version": "7",
- "when": 1747031111132,
- "tag": "0000_misty_leo",
+ "when": 1747204609794,
+ "tag": "0000_hesitant_kylun",
"breakpoints": true
}
]
diff --git a/src/app/(authenticated)/extension/sign-in/route.ts b/src/app/(authenticated)/extension/sign-in/route.ts
deleted file mode 100644
index 99f59233d9..0000000000
--- a/src/app/(authenticated)/extension/sign-in/route.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { getSignInToken } from '@/lib/server/clerk';
-import { Env } from '@/lib/server/env';
-import { auth } from '@clerk/nextjs/server';
-import { redirect } from 'next/navigation';
-import { type NextRequest } from 'next/server';
-
-export async function GET(request: NextRequest) {
- const authObj = await auth();
- const userId = authObj.userId;
- const state = request.nextUrl.searchParams.get('state') || '';
-
- if (!userId) {
- return new Response('Unauthorized', { status: 401 });
- }
-
- const signInToken = await getSignInToken(authObj.userId);
- if (!signInToken) {
- throw new Error("couldn't sign in");
- }
-
- const url = new URL(`${Env.VSCODE_EXTENSION_BASE_URL}/auth/clerk/callback`);
- url.searchParams.append('state', state);
- url.searchParams.append('code', signInToken);
- redirect(url.href);
-}
diff --git a/src/app/api/events/route.ts b/src/app/api/events/route.ts
new file mode 100644
index 0000000000..e1d5964a8c
--- /dev/null
+++ b/src/app/api/events/route.ts
@@ -0,0 +1,28 @@
+import { auth } from '@clerk/nextjs/server';
+import { NextRequest, NextResponse } from 'next/server';
+
+import { eventSchema } from '@/schemas';
+import { db } from '@/db';
+import { eventsTable } from '@/db/schema';
+
+export async function POST(request: NextRequest) {
+ const { userId } = await auth();
+
+ if (!userId) {
+ return NextResponse.json(
+ { error: 'Unauthorized request' },
+ { status: 401 },
+ );
+ }
+
+ const payload = await request.json();
+ const result = eventSchema.safeParse(payload);
+
+ if (!result.success) {
+ return NextResponse.json({ success: false });
+ }
+
+ const [record] = await db.insert(eventsTable).values(result.data).returning();
+
+ return NextResponse.json({ success: true, id: record?.id });
+}
diff --git a/src/app/api/ping/route.ts b/src/app/api/ping/route.ts
index ac768c175e..e27943b52e 100644
--- a/src/app/api/ping/route.ts
+++ b/src/app/api/ping/route.ts
@@ -4,13 +4,12 @@ import { NextResponse } from 'next/server';
import { logger } from '@/lib/server/logger';
/**
- * API endpoint for testing Clerk authentication
- * Verifies/parses JWT and logs authenticated user information
+ * API endpoint for testing Clerk authentication.
+ * Verifies/parses JWT and logs authenticated user information.
*/
export async function GET() {
const authObj = await auth();
- // If not authenticated
if (!authObj.userId) {
return NextResponse.json(
{ error: 'Unauthorized request' },
@@ -18,35 +17,18 @@ export async function GET() {
);
}
- // Get the JWT token
+ // Get the JWT token.
const token = await authObj.getToken();
- // Extract user information from the auth object
- // Only include properties that exist on the auth object
- const userInfo = {
- userId: authObj.userId,
- sessionId: authObj.sessionId,
- orgId: authObj.orgId,
- orgRole: authObj.orgRole,
- // Note: To get additional user data like email, firstName, lastName,
- // you would need to use Clerk's methods like clerkClient.users.getUser()
- };
+ // Extract user information from the auth object.
+ // Only include properties that exist on the auth object.
+ // Note: To get additional user data like email, firstName, lastName,
+ // you would need to use Clerk's methods like clerkClient.users.getUser().
+ const { userId, sessionId, orgId, orgRole } = authObj;
+ const userInfo = { userId, sessionId, orgId, orgRole };
- // Log the user information
- logger.info({
- event: 'ping_endpoint_accessed',
- userInfo: {
- userId: authObj.userId,
- sessionId: authObj.sessionId,
- orgId: authObj.orgId,
- orgRole: authObj.orgRole,
- },
- hasToken: !!token, // Just log if token exists, not the actual token for security
- });
+ // Just log if token exists, not the actual token for security.
+ logger.info({ event: 'ping_endpoint_accessed', userInfo, hasToken: !!token });
- // Return the user information
- return NextResponse.json({
- authenticated: true,
- userInfo,
- });
+ return NextResponse.json(userInfo);
}
diff --git a/src/app/extension/sign-in/DeepLink.tsx b/src/app/extension/sign-in/DeepLink.tsx
new file mode 100644
index 0000000000..221e20544d
--- /dev/null
+++ b/src/app/extension/sign-in/DeepLink.tsx
@@ -0,0 +1,51 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import Link from 'next/link';
+
+import { Button, Card, CardContent, CardFooter } from '@/components/ui';
+import { Logo } from '@/components/layout';
+
+interface DeepLinkProps {
+ vsCodeUrl: string;
+ cursorUrl: string;
+}
+
+export const DeepLink = ({ vsCodeUrl, cursorUrl }: DeepLinkProps) => {
+ const [redirectAttempted, setRedirectAttempted] = useState(false);
+
+ useEffect(() => {
+ window.location.href = vsCodeUrl;
+ const timer = setTimeout(() => setRedirectAttempted(true), 2000);
+ return () => clearTimeout(timer);
+ }, [vsCodeUrl]);
+
+ return (
+
+
+
+
+
+
+
+ You've successfully authenticated. We're attempting to
+ open VSCode with your credentials.
+
+
+
+
+
+
+
+ {redirectAttempted
+ ? 'You can close this window after VSCode opens.'
+ : 'Redirecting automatically...'}
+
+
+
+ );
+};
diff --git a/src/app/extension/sign-in/page.tsx b/src/app/extension/sign-in/page.tsx
new file mode 100644
index 0000000000..5f7e1fd52d
--- /dev/null
+++ b/src/app/extension/sign-in/page.tsx
@@ -0,0 +1,31 @@
+import { redirect } from 'next/navigation';
+import { auth } from '@clerk/nextjs/server';
+
+import { getSignInToken } from '@/lib/server/clerk';
+import { Env } from '@/lib/server/env';
+
+import { DeepLink } from './DeepLink';
+
+export default async function Page(params: {
+ searchParams: { state?: string };
+}) {
+ const { state } = await params.searchParams;
+
+ if (!state) {
+ redirect(`/sign-in`);
+ }
+
+ const { userId } = await auth();
+ const code = userId ? await getSignInToken(userId) : undefined;
+
+ if (!code) {
+ redirect(`/sign-in?state=${state}`);
+ }
+
+ const searchParams = new URLSearchParams({ state, code });
+ const path = `/auth/clerk/callback?${searchParams.toString()}`;
+ const vsCodeUrl = new URL(path, Env.VSCODE_EXTENSION_BASE_URL);
+ const cursorUrl = new URL(path, Env.CURSOR_EXTENSION_BASE_URL);
+
+ return ;
+}
diff --git a/src/app/sign-in/[[...sign-in]]/SignIn.tsx b/src/app/sign-in/[[...sign-in]]/SignIn.tsx
index a12e84d7b9..068eb7e5cd 100644
--- a/src/app/sign-in/[[...sign-in]]/SignIn.tsx
+++ b/src/app/sign-in/[[...sign-in]]/SignIn.tsx
@@ -1,5 +1,7 @@
'use client';
+import { useMemo } from 'react';
+import { useSearchParams } from 'next/navigation';
import Link from 'next/link';
import { useTheme } from 'next-themes';
import { dark } from '@clerk/themes';
@@ -10,13 +12,22 @@ import { Logo } from '@/components/layout';
export const SignIn = () => {
const { resolvedTheme } = useTheme();
const baseTheme = resolvedTheme === 'dark' ? dark : undefined;
+ const searchParams = useSearchParams();
+
+ const forceRedirectUrl = useMemo(() => {
+ const state = searchParams.get('state');
+ return state ? `/extension/sign-in?state=${state}` : undefined;
+ }, [searchParams]);
return (
-
+
);
};
diff --git a/src/app/sign-up/[[...sign-up]]/SignUp.tsx b/src/app/sign-up/[[...sign-up]]/SignUp.tsx
index 6e5b6d25f8..48c68094fb 100644
--- a/src/app/sign-up/[[...sign-up]]/SignUp.tsx
+++ b/src/app/sign-up/[[...sign-up]]/SignUp.tsx
@@ -1,5 +1,7 @@
'use client';
+import { useMemo } from 'react';
+import { useSearchParams } from 'next/navigation';
import Link from 'next/link';
import { useTheme } from 'next-themes';
import { dark } from '@clerk/themes';
@@ -10,13 +12,22 @@ import { Logo } from '@/components/layout';
export const SignUp = () => {
const { resolvedTheme } = useTheme();
const baseTheme = resolvedTheme === 'dark' ? dark : undefined;
+ const searchParams = useSearchParams();
+
+ const forceRedirectUrl = useMemo(() => {
+ const state = searchParams.get('state');
+ return state ? `/extension/sign-in?state=${state}` : undefined;
+ }, [searchParams]);
return (
-
+
);
};
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index 29f98ef9ee..903736b26f 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer",
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-sm text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer",
{
variants: {
variant: {
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
new file mode 100644
index 0000000000..7cda3847f4
--- /dev/null
+++ b/src/components/ui/card.tsx
@@ -0,0 +1,92 @@
+import * as React from 'react';
+
+import { cn } from '@/lib/utils';
+
+function Card({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+};
diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts
index b64503aeb9..e395365f1f 100644
--- a/src/components/ui/index.ts
+++ b/src/components/ui/index.ts
@@ -1,3 +1,4 @@
export * from './button';
+export * from './card';
export * from './dropdown-menu';
export * from './separator';
diff --git a/src/db/schema.ts b/src/db/schema.ts
index 94ca4c5e2a..d600638eed 100644
--- a/src/db/schema.ts
+++ b/src/db/schema.ts
@@ -3,30 +3,39 @@ import {
pgTable,
text,
timestamp,
- uniqueIndex,
+ json,
+ integer,
} from 'drizzle-orm/pg-core';
-export const organizationSchema = pgTable(
- 'organization',
- {
- id: text('id').primaryKey(),
- stripeCustomerId: text('stripe_customer_id'),
- stripeSubscriptionId: text('stripe_subscription_id'),
- stripeSubscriptionPriceId: text('stripe_subscription_price_id'),
- stripeSubscriptionStatus: text('stripe_subscription_status'),
- stripeSubscriptionCurrentPeriodEnd: bigint(
- 'stripe_subscription_current_period_end',
- { mode: 'number' },
- ),
- updatedAt: timestamp('updated_at', { mode: 'date' })
- .defaultNow()
- .$onUpdate(() => new Date())
- .notNull(),
- createdAt: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(),
- },
- (table) => ({
- stripeCustomerIdIdx: uniqueIndex('stripe_customer_id_idx').on(
- table.stripeCustomerId,
- ),
- }),
-);
+// export const organizationTable = pgTable(
+// 'organizations',
+// {
+// id: integer().primaryKey().generatedAlwaysAsIdentity(),
+// stripeCustomerId: text('stripe_customer_id'),
+// stripeSubscriptionId: text('stripe_subscription_id'),
+// stripeSubscriptionPriceId: text('stripe_subscription_price_id'),
+// stripeSubscriptionStatus: text('stripe_subscription_status'),
+// stripeSubscriptionCurrentPeriodEnd: bigint(
+// 'stripe_subscription_current_period_end',
+// { mode: 'number' },
+// ),
+// updatedAt: timestamp('updated_at', { mode: 'date' })
+// .defaultNow()
+// .$onUpdate(() => new Date())
+// .notNull(),
+// createdAt: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(),
+// },
+// (table) => [uniqueIndex('stripe_customer_id_idx').on(table.stripeCustomerId)],
+// );
+
+export const eventsTable = pgTable('events', {
+ id: integer().primaryKey().generatedAlwaysAsIdentity(),
+ type: text('type').notNull(),
+ timestamp: bigint('timestamp', { mode: 'number' }).notNull(),
+ properties: json('properties').notNull(),
+ createdAt: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(),
+ updatedAt: timestamp('updated_at', { mode: 'date' })
+ .defaultNow()
+ .$onUpdate(() => new Date())
+ .notNull(),
+});
diff --git a/src/lib/server/env.ts b/src/lib/server/env.ts
index 6912a58da3..d27e9e92ee 100644
--- a/src/lib/server/env.ts
+++ b/src/lib/server/env.ts
@@ -10,6 +10,7 @@ export const Env = createEnv({
STRIPE_WEBHOOK_SECRET: z.string().min(1),
BILLING_PLAN_ENV: z.enum(['dev', 'test', 'prod']),
VSCODE_EXTENSION_BASE_URL: z.string().min(1),
+ CURSOR_EXTENSION_BASE_URL: z.string().min(1),
},
client: {
NEXT_PUBLIC_APP_URL: z.string().optional(),
@@ -34,6 +35,7 @@ export const Env = createEnv({
STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,
BILLING_PLAN_ENV: process.env.BILLING_PLAN_ENV,
VSCODE_EXTENSION_BASE_URL: process.env.VSCODE_EXTENSION_BASE_URL,
+ CURSOR_EXTENSION_BASE_URL: process.env.CURSOR_EXTENSION_BASE_URL,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:
process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
diff --git a/src/schemas/index.ts b/src/schemas/index.ts
new file mode 100644
index 0000000000..27671fd376
--- /dev/null
+++ b/src/schemas/index.ts
@@ -0,0 +1,53 @@
+import { z } from 'zod';
+
+export const providerNames = [
+ 'anthropic',
+ 'glama',
+ 'openrouter',
+ 'bedrock',
+ 'vertex',
+ 'openai',
+ 'ollama',
+ 'vscode-lm',
+ 'lmstudio',
+ 'gemini',
+ 'openai-native',
+ 'mistral',
+ 'deepseek',
+ 'unbound',
+ 'requesty',
+ 'human-relay',
+ 'fake-ai',
+ 'xai',
+ 'groq',
+ 'chutes',
+ 'litellm',
+] as const;
+
+export const eventSchema = z.discriminatedUnion('type', [
+ z.object({
+ type: z.literal('task_created'),
+ timestamp: z.number(),
+ properties: z.object({
+ taskId: z.string(),
+ provider: z.enum(providerNames),
+ modelId: z.string(),
+ prompt: z.string(),
+ mode: z.string(),
+ }),
+ }),
+ z.object({
+ type: z.literal('completion'),
+ timestamp: z.number(),
+ properties: z.object({
+ taskId: z.string(),
+ provider: z.enum(providerNames),
+ modelId: z.string(),
+ inputTokens: z.number(),
+ outputTokens: z.number(),
+ cost: z.number(),
+ }),
+ }),
+]);
+
+export type Event = z.infer;
diff --git a/src/types/Auth.ts b/src/types/Auth.ts
deleted file mode 100644
index 7903e44bfd..0000000000
--- a/src/types/Auth.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import type { EnumValues } from './enum';
-
-export const ORG_ROLE = {
- ADMIN: 'org:admin',
- MEMBER: 'org:member',
-} as const;
-
-export type OrgRole = EnumValues;
-
-export const ORG_PERMISSION = {
- // Add Organization Permissions here.
-} as const;
-
-export type OrgPermission = EnumValues;
diff --git a/src/types/Subscription.ts b/src/types/Subscription.ts
deleted file mode 100644
index c35827213a..0000000000
--- a/src/types/Subscription.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { EnumValues } from './enum';
-
-export const PLAN_ID = {
- FREE: 'free',
- PREMIUM: 'premium',
- ENTERPRISE: 'enterprise',
-} as const;
-
-export type PlanId = EnumValues;
-
-export const BILLING_INTERVAL = {
- MONTH: 'month',
- YEAR: 'year',
-} as const;
-
-export type BillingInterval = EnumValues;
-
-export const SUBSCRIPTION_STATUS = {
- ACTIVE: 'active',
- PENDING: 'pending',
-} as const;
-
-export type IStripeSubscription = {
- stripeSubscriptionId: string | null;
- stripeSubscriptionPriceId: string | null;
- stripeSubscriptionStatus: string | null;
- stripeSubscriptionCurrentPeriodEnd: number | null;
-};
diff --git a/src/types/enum.ts b/src/types/enum.ts
deleted file mode 100644
index c273087f69..0000000000
--- a/src/types/enum.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type EnumValues = Type[keyof Type];
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
deleted file mode 100644
index 5c887a534f..0000000000
--- a/src/types/global.d.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import type { OrgPermission, OrgRole } from '@/types/auth';
-
-declare global {
- interface ClerkAuthorization {
- permission: OrgPermission;
- role: OrgRole;
- }
-}
diff --git a/tests/e2e/I18n.e2e.ts b/tests/e2e/I18n.e2e.ts
deleted file mode 100644
index 98bf37defd..0000000000
--- a/tests/e2e/I18n.e2e.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { expect, test } from '@playwright/test';
-
-test.describe('I18n', () => {
- test.describe('Language Switching', () => {
- test('should switch language from English to French using dropdown and verify text on the homepage', async ({
- page,
- }) => {
- await page.goto('/');
-
- await expect(
- page.getByText('The perfect SaaS template to build'),
- ).toBeVisible();
-
- await page.getByRole('button', { name: 'lang-switcher' }).click();
- await page.getByText('Français').click();
-
- await expect(
- page.getByText('Le parfait SaaS template pour construire'),
- ).toBeVisible();
- });
-
- test('should switch language from English to French using URL and verify text on the sign-in page', async ({
- page,
- }) => {
- await page.goto('/sign-in');
-
- await expect(page.getByText('Email address')).toBeVisible();
-
- await page.goto('/fr/sign-in');
-
- await expect(page.getByText('Adresse e-mail')).toBeVisible();
- });
- });
-});
diff --git a/tests/e2e/Sanity.check.e2e.ts b/tests/e2e/Sanity.check.e2e.ts
deleted file mode 100644
index f0c25315d3..0000000000
--- a/tests/e2e/Sanity.check.e2e.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { expect, test } from '@playwright/test';
-
-// Checkly is a tool used to monitor deployed environments, such as production or preview environments.
-// It runs end-to-end tests with the `.check.e2e.ts` extension after each deployment to ensure that the environment is up and running.
-// With Checkly, you can monitor your production environment and run `*.check.e2e.ts` tests regularly at a frequency of your choice.
-// If the tests fail, Checkly will notify you via email, Slack, or other channels of your choice.
-// On the other hand, E2E tests ending with `*.e2e.ts` are only run before deployment.
-// You can run them locally or on CI to ensure that the application is ready for deployment.
-
-// BaseURL needs to be explicitly defined in the test file.
-// Otherwise, Checkly runtime will throw an exception: `CHECKLY_INVALID_URL: Only URL's that start with http(s)`
-// You can't use `goto` function directly with a relative path like with other *.e2e.ts tests.
-// Check the example at https://feedback.checklyhq.com/changelog/new-changelog-436
-
-test.describe('Sanity', () => {
- test.describe('Static pages', () => {
- test('should display the homepage', async ({ page, baseURL }) => {
- await page.goto(`${baseURL}/`);
-
- await expect(
- page.getByText('The perfect SaaS template to build'),
- ).toBeVisible();
- });
- });
-});
diff --git a/tests/e2e/Visual.e2e.ts b/tests/e2e/Visual.e2e.ts
deleted file mode 100644
index ed75720cbe..0000000000
--- a/tests/e2e/Visual.e2e.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import percySnapshot from '@percy/playwright';
-import { expect, test } from '@playwright/test';
-
-test.describe('Visual testing', () => {
- test.describe('Static pages', () => {
- test('should take screenshot of the homepage', async ({ page }) => {
- await page.goto('/');
-
- await expect(
- page.getByText('The perfect SaaS template to build'),
- ).toBeVisible();
-
- await percySnapshot(page, 'Homepage');
- });
-
- test('should take screenshot of the French homepage', async ({ page }) => {
- await page.goto('/fr');
-
- await expect(
- page.getByText('Le parfait SaaS template pour construire'),
- ).toBeVisible();
-
- await percySnapshot(page, 'Homepage - French');
- });
- });
-});
diff --git a/tests/integration/.gitkeep b/tests/integration/.gitkeep
deleted file mode 100644
index aa2234e186..0000000000
--- a/tests/integration/.gitkeep
+++ /dev/null
@@ -1 +0,0 @@
-Place your integration tests in this folder by creating files with the `*.spec.ts` extension. These files will be automatically detected by Playwright. For a real-world example, you can check out the Next.js Boilerplate Pro at https://nextjs-boilerplate.com/pro-saas-starter-kit