5095 move onboardingstatus computation from frontend to backend (#5954)

- move front `onboardingStatus` computing to server side
- add logic to `useSetNextOnboardingStatus`
- update some missing redirections in
`usePageChangeEffectNavigateLocation`
- separate subscriptionStatus from onboardingStatus
This commit is contained in:
martmull
2024-06-28 17:32:02 +02:00
committed by GitHub
parent 1a66db5bff
commit b8f33f6f59
78 changed files with 1767 additions and 1763 deletions

View File

@ -0,0 +1,8 @@
export enum OnboardingStatus {
PLAN_REQUIRED = 'PLAN_REQUIRED',
WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION',
PROFILE_CREATION = 'PROFILE_CREATION',
SYNC_EMAIL = 'SYNC_EMAIL',
INVITE_TEAM = 'INVITE_TEAM',
COMPLETED = 'COMPLETED',
}

View File

@ -1,4 +0,0 @@
export enum OnboardingStep {
SYNC_EMAIL = 'SYNC_EMAIL',
INVITE_TEAM = 'INVITE_TEAM',
}

View File

@ -5,9 +5,19 @@ import { OnboardingResolver } from 'src/engine/core-modules/onboarding/onboardin
import { KeyValuePairModule } from 'src/engine/core-modules/key-value-pair/key-value-pair.module';
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module';
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
@Module({
imports: [DataSourceModule, UserWorkspaceModule, KeyValuePairModule],
imports: [
DataSourceModule,
WorkspaceManagerModule,
UserWorkspaceModule,
KeyValuePairModule,
EnvironmentModule,
BillingModule,
],
exports: [OnboardingService],
providers: [OnboardingService, OnboardingResolver],
})

View File

@ -1,13 +1,20 @@
import { Injectable } from '@nestjs/common';
import { KeyValuePairService } from 'src/engine/core-modules/key-value-pair/key-value-pair.service';
import { OnboardingStep } from 'src/engine/core-modules/onboarding/enums/onboarding-step.enum';
import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum';
import { User } from 'src/engine/core-modules/user/user.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
import { isDefined } from 'src/utils/is-defined';
import { BillingService } from 'src/engine/core-modules/billing/billing.service';
enum OnboardingStepValues {
SKIPPED = 'SKIPPED',
@ -26,29 +33,71 @@ type OnboardingKeyValueType = {
@Injectable()
export class OnboardingService {
constructor(
private readonly billingService: BillingService,
private readonly workspaceManagerService: WorkspaceManagerService,
private readonly userWorkspaceService: UserWorkspaceService,
private readonly keyValuePairService: KeyValuePairService<OnboardingKeyValueType>,
@InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity)
private readonly connectedAccountRepository: ConnectedAccountRepository,
@InjectWorkspaceRepository(WorkspaceMemberWorkspaceEntity)
private readonly workspaceMemberRepository: WorkspaceRepository<WorkspaceMemberWorkspaceEntity>,
) {}
private async isSyncEmailOnboardingStep(user: User, workspace: Workspace) {
private async isSubscriptionIncompleteOnboardingStatus(user: User) {
const isBillingEnabledForWorkspace =
await this.billingService.isBillingEnabledForWorkspace(
user.defaultWorkspaceId,
);
if (!isBillingEnabledForWorkspace) {
return false;
}
const currentBillingSubscription =
await this.billingService.getCurrentBillingSubscription({
workspaceId: user.defaultWorkspaceId,
});
return (
!isDefined(currentBillingSubscription) ||
currentBillingSubscription?.status === SubscriptionStatus.Incomplete
);
}
private async isWorkspaceActivationOnboardingStatus(user: User) {
return !(await this.workspaceManagerService.doesDataSourceExist(
user.defaultWorkspaceId,
));
}
private async isProfileCreationOnboardingStatus(user: User) {
const workspaceMember = await this.workspaceMemberRepository.findOneBy({
userId: user.id,
});
return (
workspaceMember &&
(!workspaceMember.name.firstName || !workspaceMember.name.lastName)
);
}
private async isSyncEmailOnboardingStatus(user: User) {
const syncEmailValue = await this.keyValuePairService.get({
userId: user.id,
workspaceId: workspace.id,
workspaceId: user.defaultWorkspaceId,
key: OnboardingStepKeys.SYNC_EMAIL_ONBOARDING_STEP,
});
const isSyncEmailSkipped = syncEmailValue === OnboardingStepValues.SKIPPED;
const connectedAccounts =
await this.connectedAccountRepository.getAllByUserId(
user.id,
workspace.id,
user.defaultWorkspaceId,
);
return !isSyncEmailSkipped && !connectedAccounts?.length;
}
private async isInviteTeamOnboardingStep(workspace: Workspace) {
private async isInviteTeamOnboardingStatus(workspace: Workspace) {
const inviteTeamValue = await this.keyValuePairService.get({
workspaceId: workspace.id,
key: OnboardingStepKeys.INVITE_TEAM_ONBOARDING_STEP,
@ -64,19 +113,28 @@ export class OnboardingService {
);
}
async getOnboardingStep(
user: User,
workspace: Workspace,
): Promise<OnboardingStep | null> {
if (await this.isSyncEmailOnboardingStep(user, workspace)) {
return OnboardingStep.SYNC_EMAIL;
async getOnboardingStatus(user: User) {
if (await this.isSubscriptionIncompleteOnboardingStatus(user)) {
return OnboardingStatus.PLAN_REQUIRED;
}
if (await this.isInviteTeamOnboardingStep(workspace)) {
return OnboardingStep.INVITE_TEAM;
if (await this.isWorkspaceActivationOnboardingStatus(user)) {
return OnboardingStatus.WORKSPACE_ACTIVATION;
}
return null;
if (await this.isProfileCreationOnboardingStatus(user)) {
return OnboardingStatus.PROFILE_CREATION;
}
if (await this.isSyncEmailOnboardingStatus(user)) {
return OnboardingStatus.SYNC_EMAIL;
}
if (await this.isInviteTeamOnboardingStatus(user.defaultWorkspace)) {
return OnboardingStatus.INVITE_TEAM;
}
return OnboardingStatus.COMPLETED;
}
async skipInviteTeamOnboardingStep(workspaceId: string) {