From 1e1ad843d33c6ddec8e5019894c3747d46f64e6a Mon Sep 17 00:00:00 2001
From: gabriellsh <40830821+gabriellsh@users.noreply.github.com>
Date: Tue, 14 Feb 2023 13:24:48 -0300
Subject: [PATCH] [IMPROVE] Setup Wizard Flow for airgapped environment
 (#28018)

---
 apps/meteor/app/api/server/v1/cloud.ts        | 15 ++++
 .../registerPreIntentWorkspaceWizard.ts       | 33 ++++++++
 .../client/views/admin/cloud/CloudPage.tsx    | 13 ++-
 .../contexts/SetupWizardContext.tsx           | 12 +--
 .../providers/SetupWizardProvider.tsx         | 84 +++++++++----------
 .../views/setupWizard/steps/AdminInfoStep.tsx | 13 +--
 .../steps/OrganizationInfoStep.tsx            |  2 +
 .../setupWizard/steps/RegisterServerStep.tsx  | 31 +++----
 .../rocketchat-i18n/i18n/en.i18n.json         |  4 +-
 packages/rest-typings/src/v1/cloud.ts         |  5 ++
 yarn.lock                                     |  6 +-
 11 files changed, 137 insertions(+), 81 deletions(-)
 create mode 100644 apps/meteor/app/cloud/server/functions/registerPreIntentWorkspaceWizard.ts

diff --git a/apps/meteor/app/api/server/v1/cloud.ts b/apps/meteor/app/api/server/v1/cloud.ts
index d7e108a7b03..38b6a722187 100644
--- a/apps/meteor/app/api/server/v1/cloud.ts
+++ b/apps/meteor/app/api/server/v1/cloud.ts
@@ -5,6 +5,7 @@ import { hasPermission, hasRole } from '../../../authorization/server';
 import { saveRegistrationData } from '../../../cloud/server/functions/saveRegistrationData';
 import { retrieveRegistrationStatus } from '../../../cloud/server/functions/retrieveRegistrationStatus';
 import { startRegisterWorkspaceSetupWizard } from '../../../cloud/server/functions/startRegisterWorkspaceSetupWizard';
+import { registerPreIntentWorkspaceWizard } from '../../../cloud/server/functions/registerPreIntentWorkspaceWizard';
 import { getConfirmationPoll } from '../../../cloud/server/functions/getConfirmationPoll';
 
 API.v1.addRoute(
@@ -60,6 +61,20 @@ API.v1.addRoute(
 	},
 );
 
+API.v1.addRoute(
+	'cloud.registerPreIntent',
+	{ authRequired: true },
+	{
+		async post() {
+			if (!hasPermission(this.userId, 'manage-cloud')) {
+				return API.v1.unauthorized();
+			}
+
+			return API.v1.success({ offline: !(await registerPreIntentWorkspaceWizard()) });
+		},
+	},
+);
+
 API.v1.addRoute(
 	'cloud.confirmationPoll',
 	{ authRequired: true },
diff --git a/apps/meteor/app/cloud/server/functions/registerPreIntentWorkspaceWizard.ts b/apps/meteor/app/cloud/server/functions/registerPreIntentWorkspaceWizard.ts
new file mode 100644
index 00000000000..c4f12aeaa9f
--- /dev/null
+++ b/apps/meteor/app/cloud/server/functions/registerPreIntentWorkspaceWizard.ts
@@ -0,0 +1,33 @@
+import { HTTP } from 'meteor/http';
+import type { IUser } from '@rocket.chat/core-typings';
+
+import { settings } from '../../../settings/server';
+import { buildWorkspaceRegistrationData } from './buildRegistrationData';
+import { SystemLogger } from '../../../../server/lib/logger/system';
+import { Users } from '../../../models/server';
+
+export async function registerPreIntentWorkspaceWizard(): Promise<boolean> {
+	const firstUser = Users.getOldest({ name: 1, emails: 1 }) as IUser | undefined;
+	const email = firstUser?.emails?.find((address) => address)?.address || '';
+
+	const regInfo = await buildWorkspaceRegistrationData(email);
+	const cloudUrl = settings.get('Cloud_Url');
+
+	try {
+		HTTP.post(`${cloudUrl}/api/v2/register/workspace/pre-intent`, {
+			data: regInfo,
+			timeout: 10 * 1000,
+		});
+
+		return true;
+	} catch (err: any) {
+		SystemLogger.error({
+			msg: 'Failed to register workspace pre-intent with Rocket.Chat Cloud',
+			url: '/api/v2/register/workspace/pre-intent',
+			...(err.response?.data && { cloudError: err.response.data }),
+			err,
+		});
+
+		return false;
+	}
+}
diff --git a/apps/meteor/client/views/admin/cloud/CloudPage.tsx b/apps/meteor/client/views/admin/cloud/CloudPage.tsx
index 4dfb19ed031..9ffdf98e4aa 100644
--- a/apps/meteor/client/views/admin/cloud/CloudPage.tsx
+++ b/apps/meteor/client/views/admin/cloud/CloudPage.tsx
@@ -11,7 +11,7 @@ import {
 } from '@rocket.chat/ui-contexts';
 import { useQuery } from '@tanstack/react-query';
 import type { ReactNode } from 'react';
-import React, { useEffect } from 'react';
+import React, { useEffect, useCallback } from 'react';
 
 import Page from '../../../components/Page';
 import ConnectToCloudSection from './ConnectToCloudSection';
@@ -28,6 +28,7 @@ const CloudPage = function CloudPage(): ReactNode {
 
 	const cloudRoute = useRoute('cloud');
 
+	const shouldOpenManualRegistration = useQueryStringParameter('register');
 	const page = useRouteParameter('page');
 
 	const errorCode = useQueryStringParameter('error_code');
@@ -95,13 +96,19 @@ const CloudPage = function CloudPage(): ReactNode {
 		acceptWorkspaceToken();
 	}, [reload, connectWorkspace, dispatchToastMessage, t, token]);
 
-	const handleManualWorkspaceRegistrationButtonClick = (): void => {
+	const handleManualWorkspaceRegistrationButtonClick = useCallback((): void => {
 		const handleModalClose = (): void => {
 			setModal(null);
 			reload();
 		};
 		setModal(<ManualWorkspaceRegistrationModal onClose={handleModalClose} />);
-	};
+	}, [setModal, reload]);
+
+	useEffect(() => {
+		if (shouldOpenManualRegistration) {
+			handleManualWorkspaceRegistrationButtonClick();
+		}
+	}, [shouldOpenManualRegistration, handleManualWorkspaceRegistrationButtonClick]);
 
 	if (result.isLoading || result.isError) {
 		return null;
diff --git a/apps/meteor/client/views/setupWizard/contexts/SetupWizardContext.tsx b/apps/meteor/client/views/setupWizard/contexts/SetupWizardContext.tsx
index 21cae1fade1..b6c09fb4a7d 100644
--- a/apps/meteor/client/views/setupWizard/contexts/SetupWizardContext.tsx
+++ b/apps/meteor/client/views/setupWizard/contexts/SetupWizardContext.tsx
@@ -1,12 +1,11 @@
 import type { ISetting } from '@rocket.chat/core-typings';
-import type { AdminInfoPage, OrganizationInfoPage, RegisteredServerPage } from '@rocket.chat/onboarding-ui';
+import type { AdminInfoPage, OrganizationInfoPage, RegisterServerPage } from '@rocket.chat/onboarding-ui';
 import type { ComponentProps, Dispatch, SetStateAction } from 'react';
 import { createContext, useContext } from 'react';
 
 type SetupWizardData = {
-	adminData: Omit<Parameters<ComponentProps<typeof AdminInfoPage>['onSubmit']>[0], 'keepPosted'>;
 	organizationData: Parameters<ComponentProps<typeof OrganizationInfoPage>['onSubmit']>[0];
-	serverData: Parameters<ComponentProps<typeof RegisteredServerPage>['onSubmit']>[0];
+	serverData: Parameters<ComponentProps<typeof RegisterServerPage>['onSubmit']>[0];
 	registrationData: {
 		device_code: string;
 		user_code: string;
@@ -28,17 +27,18 @@ type SetupWizarContextValue = {
 	goToPreviousStep: () => void;
 	goToNextStep: () => void;
 	goToStep: (step: number) => void;
-	registerAdminUser: () => Promise<void>;
+	registerAdminUser: (user: Omit<Parameters<ComponentProps<typeof AdminInfoPage>['onSubmit']>[0], 'keepPosted'>) => Promise<void>;
 	registerServer: (params: { email: string; resend?: boolean }) => Promise<void>;
+	registerPreIntent: () => Promise<void>;
 	saveWorkspaceData: () => Promise<void>;
 	saveOrganizationData: () => Promise<void>;
 	completeSetupWizard: () => Promise<void>;
+	offline: boolean;
 	maxSteps: number;
 };
 
 export const SetupWizardContext = createContext<SetupWizarContextValue>({
 	setupWizardData: {
-		adminData: { fullname: '', username: '', email: '', password: '' },
 		organizationData: {
 			organizationName: '',
 			organizationType: '',
@@ -63,11 +63,13 @@ export const SetupWizardContext = createContext<SetupWizarContextValue>({
 	goToStep: () => undefined,
 	registerAdminUser: async () => undefined,
 	registerServer: async () => undefined,
+	registerPreIntent: async () => undefined,
 	saveWorkspaceData: async () => undefined,
 	saveOrganizationData: async () => undefined,
 	validateEmail: () => true,
 	currentStep: 1,
 	completeSetupWizard: async () => undefined,
+	offline: false,
 	maxSteps: 4,
 });
 
diff --git a/apps/meteor/client/views/setupWizard/providers/SetupWizardProvider.tsx b/apps/meteor/client/views/setupWizard/providers/SetupWizardProvider.tsx
index 0fcb296ec62..4be0c9b8f85 100644
--- a/apps/meteor/client/views/setupWizard/providers/SetupWizardProvider.tsx
+++ b/apps/meteor/client/views/setupWizard/providers/SetupWizardProvider.tsx
@@ -5,7 +5,6 @@ import {
 	useLoginWithPassword,
 	useSettingSetValue,
 	useSettingsDispatch,
-	useRole,
 	useMethod,
 	useEndpoint,
 	useTranslation,
@@ -22,7 +21,6 @@ import { useParameters } from '../hooks/useParameters';
 import { useStepRouting } from '../hooks/useStepRouting';
 
 const initialData: ContextType<typeof SetupWizardContext>['setupWizardData'] = {
-	adminData: { fullname: '', username: '', email: '', password: '' },
 	organizationData: {
 		organizationName: '',
 		organizationType: '',
@@ -43,10 +41,10 @@ type HandleRegisterServer = (params: { email: string; resend?: boolean }) => Pro
 
 const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactElement => {
 	const t = useTranslation();
-	const hasAdminRole = useRole('admin');
 	const [setupWizardData, setSetupWizardData] = useState<ContextType<typeof SetupWizardContext>['setupWizardData']>(initialData);
 	const [currentStep, setCurrentStep] = useStepRouting();
 	const { isSuccess, data } = useParameters();
+	const [offline, setOffline] = useState(false);
 	const dispatchToastMessage = useToastMessageDispatch();
 	const dispatchSettings = useSettingsDispatch();
 
@@ -55,6 +53,7 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle
 	const defineUsername = useMethod('setUsername');
 	const loginWithPassword = useLoginWithPassword();
 	const setForceLogin = useSessionDispatch('forceLogin');
+	const registerPreIntentEndpoint = useEndpoint('POST', '/v1/cloud.registerPreIntent');
 	const createRegistrationIntent = useEndpoint('POST', '/v1/cloud.createRegistrationIntent');
 
 	const goToPreviousStep = useCallback(() => setCurrentStep((currentStep) => currentStep - 1), [setCurrentStep]);
@@ -72,32 +71,32 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle
 		[t],
 	);
 
-	const registerAdminUser = useCallback(async (): Promise<void> => {
-		const {
-			adminData: { fullname, username, email, password },
-		} = setupWizardData;
-		await registerUser({ name: fullname, username, email, pass: password });
-		callbacks.run('userRegistered', {});
+	const registerAdminUser = useCallback(
+		async ({ fullname, username, email, password }): Promise<void> => {
+			await registerUser({ name: fullname, username, email, pass: password });
+			callbacks.run('userRegistered', {});
 
-		try {
-			await loginWithPassword(email, password);
-		} catch (error) {
-			if (error instanceof Meteor.Error && error.error === 'error-invalid-email') {
-				dispatchToastMessage({ type: 'success', message: t('We_have_sent_registration_email') });
-				return;
-			}
-			if (error instanceof Error || typeof error === 'string') {
-				dispatchToastMessage({ type: 'error', message: error });
+			try {
+				await loginWithPassword(email, password);
+			} catch (error) {
+				if (error instanceof Meteor.Error && error.error === 'error-invalid-email') {
+					dispatchToastMessage({ type: 'success', message: t('We_have_sent_registration_email') });
+					return;
+				}
+				if (error instanceof Error || typeof error === 'string') {
+					dispatchToastMessage({ type: 'error', message: error });
+				}
+				throw error;
 			}
-			throw error;
-		}
 
-		setForceLogin(false);
+			setForceLogin(false);
 
-		await defineUsername(username);
-		await dispatchSettings([{ _id: 'Organization_Email', value: email }]);
-		callbacks.run('usernameSet', {});
-	}, [defineUsername, dispatchToastMessage, loginWithPassword, registerUser, setForceLogin, dispatchSettings, setupWizardData, t]);
+			await defineUsername(username);
+			await dispatchSettings([{ _id: 'Organization_Email', value: email }]);
+			callbacks.run('usernameSet', {});
+		},
+		[registerUser, setForceLogin, defineUsername, dispatchSettings, loginWithPassword, dispatchToastMessage, t],
+	);
 
 	const saveWorkspaceData = useCallback(async (): Promise<void> => {
 		const {
@@ -158,18 +157,6 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle
 	}, [dispatchSettings, setupWizardData]);
 
 	const registerServer: HandleRegisterServer = useMutableCallback(async ({ email, resend = false }): Promise<void> => {
-		if (!hasAdminRole) {
-			try {
-				await registerAdminUser();
-			} catch (e) {
-				if (e instanceof Error || typeof e === 'string')
-					return dispatchToastMessage({
-						type: 'error',
-						message: e,
-					});
-			}
-		}
-
 		try {
 			await saveOrganizationData();
 			const { intentData } = await createRegistrationIntent({ resend, email });
@@ -188,10 +175,17 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle
 		}
 	});
 
-	const completeSetupWizard = useMutableCallback(async (): Promise<void> => {
-		if (!hasAdminRole) {
-			await registerAdminUser();
+	const registerPreIntent = useMutableCallback(async (): Promise<void> => {
+		await saveOrganizationData();
+		try {
+			const { offline } = await registerPreIntentEndpoint();
+			setOffline(offline);
+		} catch (_) {
+			setOffline(true);
 		}
+	});
+
+	const completeSetupWizard = useMutableCallback(async (): Promise<void> => {
 		await saveOrganizationData();
 		dispatchToastMessage({ type: 'success', message: t('Your_workspace_is_ready') });
 		return setShowSetupWizard('completed');
@@ -208,6 +202,8 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle
 			goToPreviousStep,
 			goToNextStep,
 			goToStep,
+			offline,
+			registerPreIntent,
 			registerAdminUser,
 			validateEmail: _validateEmail,
 			registerServer,
@@ -218,14 +214,16 @@ const SetupWizardProvider = ({ children }: { children: ReactElement }): ReactEle
 		}),
 		[
 			setupWizardData,
-			setSetupWizardData,
 			currentStep,
 			isSuccess,
-			registerAdminUser,
-			data,
+			data.settings,
+			data.serverAlreadyRegistered,
 			goToPreviousStep,
 			goToNextStep,
 			goToStep,
+			offline,
+			registerAdminUser,
+			registerPreIntent,
 			_validateEmail,
 			registerServer,
 			saveWorkspaceData,
diff --git a/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx b/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx
index 8da51ac859c..1562c469bef 100644
--- a/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx
+++ b/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx
@@ -10,14 +10,7 @@ const AdminInfoStep = (): ReactElement => {
 	const regexpForUsernameValidation = useSetting('UTF8_User_Names_Validation');
 	const usernameRegExp = new RegExp(`^${regexpForUsernameValidation}$`);
 
-	const {
-		setupWizardData: { adminData },
-		setSetupWizardData,
-		goToNextStep,
-		currentStep,
-		validateEmail,
-		maxSteps,
-	} = useSetupWizardContext();
+	const { currentStep, validateEmail, registerAdminUser, maxSteps } = useSetupWizardContext();
 
 	// TODO: check if username exists
 	const validateUsername = (username: string): boolean | string => {
@@ -29,8 +22,7 @@ const AdminInfoStep = (): ReactElement => {
 	};
 
 	const handleSubmit: ComponentProps<typeof AdminInfoPage>['onSubmit'] = async (data) => {
-		setSetupWizardData((prevState) => ({ ...prevState, adminData: data }));
-		goToNextStep();
+		registerAdminUser(data);
 	};
 
 	return (
@@ -40,7 +32,6 @@ const AdminInfoStep = (): ReactElement => {
 			validateUsername={validateUsername}
 			validateEmail={validateEmail}
 			currentStep={currentStep}
-			initialValues={adminData}
 			stepCount={maxSteps}
 			onSubmit={handleSubmit}
 		/>
diff --git a/apps/meteor/client/views/setupWizard/steps/OrganizationInfoStep.tsx b/apps/meteor/client/views/setupWizard/steps/OrganizationInfoStep.tsx
index 41a304db40e..73ea00c1aa4 100644
--- a/apps/meteor/client/views/setupWizard/steps/OrganizationInfoStep.tsx
+++ b/apps/meteor/client/views/setupWizard/steps/OrganizationInfoStep.tsx
@@ -37,6 +37,7 @@ const OrganizationInfoStep = (): ReactElement => {
 		goToNextStep,
 		completeSetupWizard,
 		currentStep,
+		registerPreIntent,
 		skipCloudRegistration,
 		maxSteps,
 	} = useSetupWizardContext();
@@ -51,6 +52,7 @@ const OrganizationInfoStep = (): ReactElement => {
 			return completeSetupWizard();
 		}
 		setSetupWizardData((prevState) => ({ ...prevState, organizationData: data }));
+		await registerPreIntent();
 		goToNextStep();
 	};
 
diff --git a/apps/meteor/client/views/setupWizard/steps/RegisterServerStep.tsx b/apps/meteor/client/views/setupWizard/steps/RegisterServerStep.tsx
index 10dc50e154d..6bc2e25259b 100644
--- a/apps/meteor/client/views/setupWizard/steps/RegisterServerStep.tsx
+++ b/apps/meteor/client/views/setupWizard/steps/RegisterServerStep.tsx
@@ -1,4 +1,5 @@
-import { RegisteredServerPage, StandaloneServerPage } from '@rocket.chat/onboarding-ui';
+import { RegisterServerPage, StandaloneServerPage } from '@rocket.chat/onboarding-ui';
+import { useRoute } from '@rocket.chat/ui-contexts';
 import type { ReactElement, ComponentProps } from 'react';
 import React, { useState } from 'react';
 
@@ -10,18 +11,18 @@ const SERVER_OPTIONS = {
 };
 
 const RegisterServerStep = (): ReactElement => {
-	const {
-		goToPreviousStep,
-		currentStep,
-		setSetupWizardData,
-		setupWizardData: { adminData },
-		registerServer,
-		maxSteps,
-		completeSetupWizard,
-	} = useSetupWizardContext();
+	const { goToPreviousStep, currentStep, setSetupWizardData, registerServer, maxSteps, offline, completeSetupWizard } =
+		useSetupWizardContext();
 	const [serverOption, setServerOption] = useState(SERVER_OPTIONS.REGISTERED);
 
-	const handleRegister: ComponentProps<typeof RegisteredServerPage>['onSubmit'] = async (data) => {
+	const router = useRoute('cloud');
+
+	const handleRegisterOffline: ComponentProps<typeof RegisterServerPage>['onSubmit'] = async () => {
+		await completeSetupWizard();
+		router.push({}, { register: 'true' });
+	};
+
+	const handleRegister: ComponentProps<typeof RegisterServerPage>['onSubmit'] = async (data) => {
 		if (data.registerType !== 'standalone') {
 			setSetupWizardData((prevState) => ({ ...prevState, serverData: data }));
 			await registerServer(data);
@@ -46,13 +47,13 @@ const RegisterServerStep = (): ReactElement => {
 	}
 
 	return (
-		<RegisteredServerPage
-			onClickContinue={(): void => setServerOption(SERVER_OPTIONS.STANDALONE)}
+		<RegisterServerPage
+			onClickRegisterLater={(): void => setServerOption(SERVER_OPTIONS.STANDALONE)}
 			onBackButtonClick={goToPreviousStep}
 			stepCount={maxSteps}
-			onSubmit={handleRegister}
+			onSubmit={offline ? handleRegisterOffline : handleRegister}
 			currentStep={currentStep}
-			initialValues={{ email: adminData.email }}
+			offline={offline}
 		/>
 	);
 };
diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
index 0592046ccfb..95576e93499 100644
--- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -5423,6 +5423,7 @@
   "onboarding.component.form.action.next": "Next",
   "onboarding.component.form.action.skip": "Skip this step",
   "onboarding.component.form.action.register": "Register",
+  "onboarding.component.form.action.registerNow": "Register now",
   "onboarding.component.form.action.confirm": "Confirm",
   "onboarding.component.form.termsAndConditions": "I agree with <1>Terms and Conditions</1> and <3>Privacy Policy</3>",
   "onboarding.component.emailCodeFallback": "Didn’t receive email? <1>Resend</1> or <3>Change email</3>",
@@ -5486,7 +5487,8 @@
   "onboarding.form.registeredServerForm.fields.accountEmail.tooltipLabel": "To register your server, we need to connect it to your cloud account. If you already have one - we will link it automatically. Otherwise, a new account will be created",
   "onboarding.form.registeredServerForm.fields.accountEmail.inputPlaceholder": "Please enter your Email",
   "onboarding.form.registeredServerForm.keepInformed": "Keep me informed about news and events",
-  "onboarding.form.registeredServerForm.continueStandalone": "Continue as standalone",
+  "onboarding.form.registeredServerForm.registerLater": "Register later",
+  "onboarding.form.registeredServerForm.notConnectedToInternet": "The server is not connected to the internet, so you’ll have to do an offline registration for this workspace.",
   "onboarding.form.registeredServerForm.agreeToReceiveUpdates": "By registering I agree to receive relevant product and security updates",
   "onboarding.form.standaloneServerForm.title": "Standalone Server Confirmation",
   "onboarding.form.standaloneServerForm.servicesUnavailable": "Some of the services will be unavailable or will require manual setup",
diff --git a/packages/rest-typings/src/v1/cloud.ts b/packages/rest-typings/src/v1/cloud.ts
index 15a73d9da09..90664dcc243 100644
--- a/packages/rest-typings/src/v1/cloud.ts
+++ b/packages/rest-typings/src/v1/cloud.ts
@@ -75,6 +75,11 @@ export type CloudEndpoints = {
 			intentData: CloudRegistrationIntentData;
 		};
 	};
+	'/v1/cloud.registerPreIntent': {
+		POST: () => {
+			offline: boolean;
+		};
+	};
 	'/v1/cloud.confirmationPoll': {
 		GET: (params: CloudConfirmationPoll) => {
 			pollData: CloudConfirmationPollData;
diff --git a/yarn.lock b/yarn.lock
index 3c767495350..6f24e9cb899 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7272,8 +7272,8 @@ __metadata:
   linkType: hard
 
 "@rocket.chat/onboarding-ui@npm:next":
-  version: 0.32.0-dev.245
-  resolution: "@rocket.chat/onboarding-ui@npm:0.32.0-dev.245"
+  version: 0.32.0-dev.275
+  resolution: "@rocket.chat/onboarding-ui@npm:0.32.0-dev.275"
   dependencies:
     i18next: ~21.6.16
     react-hook-form: ~7.27.1
@@ -7289,7 +7289,7 @@ __metadata:
     react: 17.0.2
     react-dom: 17.0.2
     react-i18next: ~11.15.4
-  checksum: eef580b286ceab884e40c660269521e7a7a814803cd5b62a2b4968dcb4b41f180149dc97a708d79dd62ae5d27ddae2d9acadabf81e8cbdd6aae7bef1ed38873b
+  checksum: c5007a1e6dacd404e192327858a7b76da938380370df55da9ad51fdc055888d8fcc059117b21091826da02b043fd03aa3d0bcdc5eb573d7c330d7deef8edd196
   languageName: node
   linkType: hard
 
-- 
GitLab