cb49a7a053
https://github.com/user-attachments/assets/cc7b1d10-7495-4f21-9311-4c22c0f14771 Adds the full-screen loading screen shown while a new workspace is being created in the v2 sign-up flow (`SignInUpV2`), building on the v2 "Create your workspace" step. How it works: - Submitting the v2 create-workspace form marks the flow as v2 (`isOnboardingV2State`) and creates the workspace. The flag is carried across the cross-subdomain redirect with an `onboardingV2=true` URL param, so v2 users land on a new `/workspace-activation-v2` route instead of v1's `/workspace-activation`. - `WorkspaceActivationV2` runs the real `activateWorkspace` mutation on mount and renders the loader: a pulsing Twenty logomark above a stack of status messages that shift up one at a time, cycling once per second. There is no faked/minimum duration; it advances to the next onboarding step as soon as the workspace is activated. - On activation failure it shows a "Workspace creation failed" screen with a Retry button. v1 onboarding is unchanged. Storybook: `Modules/Auth/SignInUpWorkspaceActivationV2`. Note: The flashes will be fixed in later PRs <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22152?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. -->
73 lines
2.4 KiB
TypeScript
73 lines
2.4 KiB
TypeScript
import { useCallback } from 'react';
|
|
|
|
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
|
|
import { isOnboardingV2State } from '@/auth/states/isOnboardingV2State';
|
|
import { returnToPathState } from '@/auth/states/returnToPathState';
|
|
import { type BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
|
|
import { isValidReturnToPath } from '@/auth/utils/isValidReturnToPath';
|
|
import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/settings/billing/constants/BillingCheckoutSessionDefaultValue';
|
|
import deepEqual from 'deep-equal';
|
|
import { useStore } from 'jotai';
|
|
|
|
export const useInitializeQueryParamState = () => {
|
|
const store = useStore();
|
|
const initializeQueryParamState = useCallback(() => {
|
|
const handlers: Record<string, (value: string) => void> = {
|
|
billingCheckoutSession: (value: string) => {
|
|
const billingCheckoutSession = store.get(
|
|
billingCheckoutSessionState.atom,
|
|
);
|
|
|
|
try {
|
|
const parsedValue = JSON.parse(decodeURIComponent(value));
|
|
|
|
if (
|
|
typeof parsedValue === 'object' &&
|
|
parsedValue !== null &&
|
|
'plan' in parsedValue &&
|
|
'interval' in parsedValue &&
|
|
'requirePaymentMethod' in parsedValue &&
|
|
!deepEqual(billingCheckoutSession, parsedValue)
|
|
) {
|
|
store.set(
|
|
billingCheckoutSessionState.atom,
|
|
parsedValue as BillingCheckoutSession,
|
|
);
|
|
}
|
|
} catch (error) {
|
|
// oxlint-disable-next-line no-console
|
|
console.error(
|
|
'Failed to parse billingCheckoutSession from URL',
|
|
error,
|
|
);
|
|
store.set(
|
|
billingCheckoutSessionState.atom,
|
|
BILLING_CHECKOUT_SESSION_DEFAULT_VALUE,
|
|
);
|
|
}
|
|
},
|
|
returnToPath: (value: string) => {
|
|
if (isValidReturnToPath(value)) {
|
|
store.set(returnToPathState.atom, value);
|
|
}
|
|
},
|
|
onboardingV2: (value: string) => {
|
|
if (value === 'true') {
|
|
store.set(isOnboardingV2State.atom, true);
|
|
}
|
|
},
|
|
};
|
|
|
|
const queryParams = new URLSearchParams(window.location.search);
|
|
|
|
for (const [paramName, handler] of Object.entries(handlers)) {
|
|
const value = queryParams.get(paramName);
|
|
if (value !== null) {
|
|
handler(value);
|
|
}
|
|
}
|
|
}, [store]);
|
|
|
|
return { initializeQueryParamState };
|
|
};
|