({
variant="outline"
role="combobox"
aria-expanded={open}
- className={cn('justify-between', className)}
+ className={cn(
+ 'justify-between',
+ !!error && 'border-destructive',
+ className
+ )}
>
{Icon ? : null}
diff --git a/packages/trpc/src/routers/onboarding.ts b/packages/trpc/src/routers/onboarding.ts
index 042f91a0..5a8c9135 100644
--- a/packages/trpc/src/routers/onboarding.ts
+++ b/packages/trpc/src/routers/onboarding.ts
@@ -1,5 +1,6 @@
import { randomUUID } from 'crypto';
import { clerkClient } from '@clerk/fastify';
+import type { z } from 'zod';
import { hashPassword, slug, stripTrailingSlash } from '@openpanel/common';
import { db, getId } from '@openpanel/db';
@@ -8,6 +9,27 @@ import { zOnboardingProject } from '@openpanel/validation';
import { createTRPCRouter, protectedProcedure } from '../trpc';
+async function createOrGetOrganization(
+ input: z.infer,
+ userId: string
+) {
+ if (input.organizationSlug) {
+ return await clerkClient.organizations.getOrganization({
+ slug: input.organizationSlug,
+ });
+ }
+
+ if (input.organization) {
+ return await clerkClient.organizations.createOrganization({
+ name: input.organization,
+ slug: slug(input.organization),
+ createdBy: userId,
+ });
+ }
+
+ return null;
+}
+
export const onboardingRouter = createTRPCRouter({
project: protectedProcedure
.input(zOnboardingProject)
@@ -17,13 +39,12 @@ export const onboardingRouter = createTRPCRouter({
if (input.app) types.push('app');
if (input.backend) types.push('backend');
- const organization = await clerkClient.organizations.createOrganization({
- name: input.organization,
- slug: slug(input.organization),
- createdBy: ctx.session.userId,
- });
+ const organization = await createOrGetOrganization(
+ input,
+ ctx.session.userId
+ );
- if (!organization.slug) {
+ if (!organization?.slug) {
throw new Error('Organization slug is missing');
}
diff --git a/packages/validation/src/index.ts b/packages/validation/src/index.ts
index 0f49491a..029023c4 100644
--- a/packages/validation/src/index.ts
+++ b/packages/validation/src/index.ts
@@ -100,7 +100,8 @@ export const zCreateReference = z.object({
export const zOnboardingProject = z
.object({
- organization: z.string().min(3),
+ organization: z.string().optional(),
+ organizationSlug: z.string().optional(),
project: z.string().min(3),
domain: z.string().url().or(z.literal('').or(z.null())),
website: z.boolean(),
@@ -108,6 +109,19 @@ export const zOnboardingProject = z
backend: z.boolean(),
})
.superRefine((data, ctx) => {
+ if (!data.organization && !data.organizationSlug) {
+ ctx.addIssue({
+ code: 'custom',
+ message: 'Organization is required',
+ path: ['organization'],
+ });
+ ctx.addIssue({
+ code: 'custom',
+ message: 'Organization is required',
+ path: ['organizationSlug'],
+ });
+ }
+
if (data.website && !data.domain) {
ctx.addIssue({
code: 'custom',