Files
calendar/packages/lib/server/repository/organizationOnboarding.ts
T
sean-brydonGitHubcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>Hariom Balhara
fa35cc5210 chore: organization onboarding refactor (#24381)
* feat: redirect to new onboarding flow

* Getting started

* Brand details

* Preview organization brands

* Orgs team pages

* Invite team steps

* Move to global zustand store

* Few darkmdoe fixes

* Wip onboarding + stripe flow

* Default plan state

Server Action for gettting slug satus of org

* Remove onboardingId

* Confirmation prompt

* Update old onboarding flow handlers to handle new fields

* update onboarding hook

* Filter out organization section for none -company emails

* Match placeholders to users domain

* Drop migration

* Wip new onboarding intent

* WIP flow for self-hosted. Same service call just split logic

* WIP

* Add TODO

* Use onboarding user type instead of trpc session

* WIP

* WIP

* pass role and team name from onboarding to save in schema

* Add test to ensure role + name + team are persisted into onboarding table

* migrate roles to enum values

* Update ENUM

* Fix type error

* Redirect if flag is disabled

* Remove web

* WIP

* WIP

* Fix migration

* Fix calls

* User onboarding User types instead of trpc session

* Fix factory tests

* Fix flow for self hoste

* Type error

* More type fixes

* Fix handler tests

* Fix enum return type being different

* Use consistant types across the oganization stuff

* Fix

* Use TEAM_BILLING for e2e test

* Refactor is not company email and add tests

* Fix

* Fix

* Refactor flow to submit after form complete

* Fix flow with billing disabled

* Fix tests

* Apply suggestion from @coderabbitai[bot]

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Rename and move test files

* WIP

* Fix types

* Update repo paths + tests

* Move to service folder

* Fix tests

* Fix types

* Remove old test files

* Restore lock

* Fix path

* Fix tests with new paths and factory logic

* Fix updaetdAt

* WIP onboardingID isolation

* Fix e2e test

* verify test

* Code rabbit

* Rename SelfHostedOnboardongService -> SelfHostedOrganizationOnboardingService

* Fix stores

* Fix type error

* Fix types

* remove tsignore

* Apply suggestion from @coderabbitai[bot]

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* NITS

* Add the logic to auto complete admin org when billing enabled

* Fix store being weird

* We need to return the parsed value

* fixes

* sync from db always

* Add onboardingSgtore tests

* fix test

* remove step and status

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2025-10-18 14:13:30 +00:00

144 lines
4.2 KiB
TypeScript

import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import { prisma } from "@calcom/prisma";
import type { BillingPeriod } from "@calcom/prisma/enums";
type OnboardingId = string;
export type CreateOrganizationOnboardingInput = {
createdById: number;
organizationId?: number | null;
billingPeriod: BillingPeriod;
pricePerSeat: number;
seats: number;
orgOwnerEmail: string;
name: string;
slug: string;
logo?: string | null;
bio?: string | null;
brandColor?: string | null;
bannerUrl?: string | null;
stripeCustomerId?: string;
stripeSubscriptionId?: string;
stripeSubscriptionItemId?: string;
invitedMembers?: { email: string; name?: string }[];
teams?: { id: number; name: string; isBeingMigrated: boolean; slug: string | null }[];
error?: string | null;
isDomainConfigured?: boolean;
isComplete?: boolean;
};
export class OrganizationOnboardingRepository {
static async create(data: CreateOrganizationOnboardingInput) {
logger.debug("Creating organization onboarding", safeStringify(data));
return await prisma.organizationOnboarding.create({
// HEKOP
data: {
billingPeriod: data.billingPeriod,
pricePerSeat: data.pricePerSeat,
seats: data.seats,
orgOwnerEmail: data.orgOwnerEmail,
name: data.name,
slug: data.slug,
logo: data.logo,
bio: data.bio,
brandColor: data.brandColor,
bannerUrl: data.bannerUrl,
stripeCustomerId: data.stripeCustomerId,
stripeSubscriptionId: data.stripeSubscriptionId,
invitedMembers: data.invitedMembers || [],
teams: data.teams || [],
createdById: data.createdById,
},
});
}
static async findByStripeCustomerId(stripeCustomerId: string) {
logger.debug(
"Finding organization onboarding by stripe customer id",
safeStringify({ stripeCustomerId })
);
return await prisma.organizationOnboarding.findUnique({
where: {
stripeCustomerId,
},
});
}
static async findById(id: OnboardingId) {
logger.debug("Finding organization onboarding by id", safeStringify({ id }));
return await prisma.organizationOnboarding.findUnique({
where: {
id,
},
});
}
static async findByOrgOwnerEmail(email: string) {
logger.debug("Finding organization onboarding by org owner email", safeStringify({ email }));
return await prisma.organizationOnboarding.findUnique({
where: {
orgOwnerEmail: email,
},
});
}
// TODO: This method should be moved to OrganizationOnboardingService
static async markAsComplete(id: OnboardingId) {
logger.debug("Marking organization onboarding as complete", { id });
return await prisma.organizationOnboarding.update({
where: {
id,
},
data: {
error: null,
isComplete: true,
},
});
}
static async update(id: OnboardingId, data: Partial<CreateOrganizationOnboardingInput>) {
logger.debug("Updating organization onboarding", safeStringify({ id, data }));
// We don't want to update the createdById field in update
const { organizationId, createdById: _, ...rest } = data;
return await prisma.organizationOnboarding.update({
where: {
id,
},
data: {
...rest,
...(organizationId ? { organization: { connect: { id: organizationId } } } : {}),
updatedAt: new Date(),
},
});
}
static async findByOrganizationId(organizationId: number) {
logger.debug("Finding organization onboarding by organization id", safeStringify({ organizationId }));
return await prisma.organizationOnboarding.findUnique({
where: {
organizationId,
},
});
}
static async findAllBySlug(slug: string) {
logger.debug("Finding all organization onboardings by slug", safeStringify({ slug }));
return await prisma.organizationOnboarding.findMany({
where: {
slug,
},
});
}
static async delete(id: OnboardingId) {
logger.debug("Deleting organization onboarding", { id });
return await prisma.organizationOnboarding.delete({
where: {
id,
},
});
}
}