2e6077383b
https://github.com/user-attachments/assets/5326d48f-1842-4db1-bc7c-94852145c035 <img width="838" height="754" alt="CleanShot 2026-06-30 at 16 25 05@2x" src="https://github.com/user-attachments/assets/5c7d53d7-4d65-4e35-aed1-edf0c104e140" /> Adds an "Install your first apps" step to the V2 onboarding, shown right after import-contacts. It lets users opt into installing marketplace apps (Call recorder and People Data Labs for now) during onboarding. - New backend `OnboardingStatus.APPS_INSTALLATION` (between SYNC_EMAIL and PROFILE_CREATION); V1 auto-skips it. - The primary button sends the selected app ids to the server via `triggerInstallAppsOnboardingStep`, which enqueues a dedicated job that installs them asynchronously so onboarding isn't blocked. Skip continues without installing. - The workspace is credited per app on successful installation. Credits are env-driven via `ONBOARDING_INSTALL_APPS_CREDITS_REWARD_PER_APP`, shown as "Earn +N free credits (1 per tool)". <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22347?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. -->
36 lines
837 B
TypeScript
36 lines
837 B
TypeScript
import { useTriggerInstallAppsOnboardingStep } from '@/onboarding/hooks/useTriggerInstallAppsOnboardingStep';
|
|
import { useEffect, useRef } from 'react';
|
|
|
|
type InstallAppsAutoSkipEffectProps = {
|
|
onError: () => void;
|
|
};
|
|
|
|
export const InstallAppsAutoSkipEffect = ({
|
|
onError,
|
|
}: InstallAppsAutoSkipEffectProps) => {
|
|
const triggerInstallAppsOnboardingStep =
|
|
useTriggerInstallAppsOnboardingStep();
|
|
|
|
// oxlint-disable-next-line twenty/no-state-useref
|
|
const hasSkippedRef = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (hasSkippedRef.current) {
|
|
return;
|
|
}
|
|
hasSkippedRef.current = true;
|
|
|
|
const skip = async () => {
|
|
try {
|
|
await triggerInstallAppsOnboardingStep([]);
|
|
} catch {
|
|
onError();
|
|
}
|
|
};
|
|
|
|
void skip();
|
|
}, [triggerInstallAppsOnboardingStep, onError]);
|
|
|
|
return null;
|
|
};
|