Add v2 onboarding invite team page (#22229)

<img width="3024" height="1500" alt="CleanShot 2026-06-26 at 18 09
47@2x"
src="https://github.com/user-attachments/assets/e91f30a5-2763-42a0-9abf-d9fa8400870c"
/>


Adds the v2 onboarding **Invite team** page (`INVITE_TEAM`), shown right
after the create-profile step for the onboarding-v2 cohort. It renders
full-screen under `BlankLayout` via the shared `OnboardingV2Layout`,
matching the Figma (340px column, email inputs with inline remove, dark
Invite, Skip).

Reuses all v1 invite-team logic via a new `useInviteTeam` hook (v1
`InviteTeam` now consumes it too; its UI is unchanged). Routing mirrors
`SyncEmailsV2`/`CreateProfileV2`: new `AppPath.InviteTeamV2`, lazy
route, and an `isOnboardingV2`-gated branch in
`usePageChangeEffectNavigateLocation` (+ tests and a Storybook story).

No backend changes.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22229?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Raphaël Bosi
2026-06-29 13:53:07 +02:00
committed by GitHub
parent 921c528cc3
commit db7d8172f7
24 changed files with 559 additions and 169 deletions
@@ -85,6 +85,11 @@ describe('ClientConfigController', () => {
api: {
mutationMaximumAffectedRecords: 100,
},
onboarding: {
importContactsCreditsReward: 2,
inviteTeamMaxCreditsReward: 9,
inviteTeamCreditsRewardPerUser: 3,
},
isAttachmentPreviewEnabled: true,
analyticsEnabled: false,
canManageFeatureFlags: true,
@@ -208,6 +208,14 @@ export class ApiConfig {
mutationMaximumAffectedRecords: number;
}
export class OnboardingConfig {
importContactsCreditsReward: number;
inviteTeamMaxCreditsReward: number;
inviteTeamCreditsRewardPerUser: number;
}
@ObjectType()
export class PublicFeatureFlagMetadata {
@Field(() => String)
@@ -291,6 +299,8 @@ export class ClientConfig {
@Field(() => ApiConfig)
api: ApiConfig;
onboarding: OnboardingConfig;
@Field(() => Boolean)
canManageFeatureFlags: boolean;
@@ -92,6 +92,9 @@ describe('ClientConfigService', () => {
CAPTCHA_DRIVER: CaptchaDriverType.GOOGLE_RECAPTCHA,
CAPTCHA_SITE_KEY: 'site-key-123',
MUTATION_MAXIMUM_AFFECTED_RECORDS: 1000,
ONBOARDING_IMPORT_CONTACTS_CREDITS_REWARD: 2_000_000,
ONBOARDING_INVITE_TEAM_MAX_CREDITS_REWARD: 9_000_000,
ONBOARDING_INVITE_TEAM_CREDITS_REWARD_PER_USER: 3_000_000,
IS_ATTACHMENT_PREVIEW_ENABLED: true,
ANALYTICS_ENABLED: true,
MESSAGING_PROVIDER_MICROSOFT_ENABLED: false,
@@ -165,6 +168,11 @@ describe('ClientConfigService', () => {
api: {
mutationMaximumAffectedRecords: 1000,
},
onboarding: {
importContactsCreditsReward: 2,
inviteTeamMaxCreditsReward: 9,
inviteTeamCreditsRewardPerUser: 3,
},
isAttachmentPreviewEnabled: true,
analyticsEnabled: true,
canManageFeatureFlags: true,
@@ -15,6 +15,7 @@ import { DomainServerConfigService } from 'src/engine/core-modules/domain/domain
import { EmailingDomainDriver } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-driver.type';
import { PUBLIC_FEATURE_FLAGS } from 'src/engine/core-modules/feature-flag/constants/public-feature-flag.const';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { toDisplayCredits } from 'src/engine/core-modules/usage/utils/to-display-credits.util';
import {
AUTO_SELECT_FAST_MODEL_ID,
AUTO_SELECT_SMART_MODEL_ID,
@@ -218,6 +219,23 @@ export class ClientConfigService {
'MUTATION_MAXIMUM_AFFECTED_RECORDS',
),
},
onboarding: {
importContactsCreditsReward: toDisplayCredits(
this.twentyConfigService.get(
'ONBOARDING_IMPORT_CONTACTS_CREDITS_REWARD',
),
),
inviteTeamMaxCreditsReward: toDisplayCredits(
this.twentyConfigService.get(
'ONBOARDING_INVITE_TEAM_MAX_CREDITS_REWARD',
),
),
inviteTeamCreditsRewardPerUser: toDisplayCredits(
this.twentyConfigService.get(
'ONBOARDING_INVITE_TEAM_CREDITS_REWARD_PER_USER',
),
),
},
isAttachmentPreviewEnabled: this.twentyConfigService.get(
'IS_ATTACHMENT_PREVIEW_ENABLED',
),
@@ -5,6 +5,7 @@ import {
IsDateString,
IsDefined,
IsEnum,
IsInt,
IsNotEmpty,
IsOptional,
IsString,
@@ -932,6 +933,39 @@ export class ConfigVariables {
@IsOptional()
BILLING_USAGE_CAP_CLICKHOUSE_ENABLED = false;
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.BILLING_CONFIG,
description:
'Free credits granted for completing the import-contacts onboarding step (in microCredits)',
type: ConfigVariableType.NUMBER,
})
@CastToPositiveNumber()
@IsInt()
@IsOptional()
ONBOARDING_IMPORT_CONTACTS_CREDITS_REWARD = 2_000_000;
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.BILLING_CONFIG,
description:
'Maximum free credits granted for completing the invite-team onboarding step (in microCredits)',
type: ConfigVariableType.NUMBER,
})
@CastToPositiveNumber()
@IsInt()
@IsOptional()
ONBOARDING_INVITE_TEAM_MAX_CREDITS_REWARD = 9_000_000;
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.BILLING_CONFIG,
description:
'Free credits granted per user invited during the invite-team onboarding step (in microCredits)',
type: ConfigVariableType.NUMBER,
})
@CastToPositiveNumber()
@IsInt()
@IsOptional()
ONBOARDING_INVITE_TEAM_CREDITS_REWARD_PER_USER = 3_000_000;
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.SERVER_CONFIG,
description: 'Url for the frontend application',