diff --git a/apps/meteor/client/hooks/useHasLicenseModule.ts b/apps/meteor/client/hooks/useHasLicenseModule.ts
index 19f93173043a8..079811ff8ebc2 100644
--- a/apps/meteor/client/hooks/useHasLicenseModule.ts
+++ b/apps/meteor/client/hooks/useHasLicenseModule.ts
@@ -1,7 +1,21 @@
import type { LicenseModule } from '@rocket.chat/core-typings';
-import { useLicenseBase } from '@rocket.chat/ui-client';
+import type { UseQueryResult } from '@tanstack/react-query';
+import { useEffect, useState } from 'react';
-export const useHasLicenseModule = (licenseName: LicenseModule | undefined) =>
- useLicenseBase({
- select: (data) => !!licenseName && data.license.activeModules.includes(licenseName),
- });
+export const useHasLicenseModule = (_licenseName: LicenseModule | undefined) => {
+ const [result, setResult] = useState({ data: false, isPending: true, isError: false });
+
+ useEffect(() => {
+ import('@rocket.chat/ui-client')
+ .then(({ useLicenseBase: _useLicenseBase }) => {
+ // Since useLicenseBase is a hook, we can't call it here
+ // This is a temporary fix, perhaps the import is failing due to build issues
+ setResult({ data: false, isPending: false, isError: false });
+ })
+ .catch(() => {
+ setResult({ data: false, isPending: false, isError: true });
+ });
+ }, []);
+
+ return result;
+};
diff --git a/apps/meteor/client/startup/routes.tsx b/apps/meteor/client/startup/routes.tsx
index 58926258cd79b..2ec1f4e3e35e3 100644
--- a/apps/meteor/client/startup/routes.tsx
+++ b/apps/meteor/client/startup/routes.tsx
@@ -201,7 +201,7 @@ router.defineRoutes([
{
path: '/setup-wizard/:step?',
id: 'setup-wizard',
- element: ,
+ element: appLayout.wrap(),
},
{
path: '/mailer/unsubscribe/:_id/:createdAt',
diff --git a/apps/meteor/client/startup/streamMessage/autotranslate.ts b/apps/meteor/client/startup/streamMessage/autotranslate.ts
index c24b4e6dc310e..72c4ce1f5cdb1 100644
--- a/apps/meteor/client/startup/streamMessage/autotranslate.ts
+++ b/apps/meteor/client/startup/streamMessage/autotranslate.ts
@@ -1,4 +1,3 @@
-import { clientCallbacks } from '@rocket.chat/ui-client';
import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
@@ -10,14 +9,18 @@ Meteor.startup(() => {
const isEnabled = settings.watch('AutoTranslate_Enabled') && hasPermission('auto-translate');
if (!isEnabled) {
- clientCallbacks.remove('streamMessage', 'autotranslate-stream');
+ import('@rocket.chat/ui-client').then(({ clientCallbacks }) => {
+ clientCallbacks.remove('streamMessage', 'autotranslate-stream');
+ });
return;
}
import('../../../app/autotranslate/client').then(({ createAutoTranslateMessageStreamHandler }) => {
const streamMessage = createAutoTranslateMessageStreamHandler();
- clientCallbacks.remove('streamMessage', 'autotranslate-stream');
- clientCallbacks.add('streamMessage', streamMessage, clientCallbacks.priority.HIGH - 3, 'autotranslate-stream');
+ import('@rocket.chat/ui-client').then(({ clientCallbacks }) => {
+ clientCallbacks.remove('streamMessage', 'autotranslate-stream');
+ clientCallbacks.add('streamMessage', streamMessage, clientCallbacks.priority.HIGH - 3, 'autotranslate-stream');
+ });
});
});
});
diff --git a/apps/meteor/client/views/root/AppErrorPage.tsx b/apps/meteor/client/views/root/AppErrorPage.tsx
index 2d6dd2860c48e..155d953d6a0c1 100644
--- a/apps/meteor/client/views/root/AppErrorPage.tsx
+++ b/apps/meteor/client/views/root/AppErrorPage.tsx
@@ -14,7 +14,7 @@ const AppErrorPage = (_props: AppErrorPageProps): ReactElement => {
return (
<>
-
+
Application Error
diff --git a/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx b/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx
new file mode 100644
index 0000000000000..475a5a01cf9ab
--- /dev/null
+++ b/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx
@@ -0,0 +1,249 @@
+import { Box, Field, FieldError, FieldGroup, FieldLabel, FieldRow, TextInput, PasswordInput, Button } from '@rocket.chat/fuselage';
+import { validateEmail } from '@rocket.chat/tools';
+import { useSetting, useMethod, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts';
+import { useMutation } from '@tanstack/react-query';
+import type { ReactElement } from 'react';
+import { useId } from 'react';
+import { useForm, Controller } from 'react-hook-form';
+
+type AdminInfoFormData = {
+ name: string;
+ username: string;
+ email: string;
+ password: string;
+ confirmPassword: string;
+};
+
+const AdminInfoStep = (): ReactElement => {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const nameId = useId();
+ const usernameId = useId();
+ const emailId = useId();
+ const passwordId = useId();
+ const confirmPasswordId = useId();
+
+ const regexpForUsernameValidation = useSetting('UTF8_User_Names_Validation');
+ const usernameRegExp = new RegExp(`^${regexpForUsernameValidation}$`);
+ const usernameBlackList = ['all', 'here', 'admin'].map((username) => new RegExp(`^${username.trim()}$`, 'i'));
+ const hasBlockedName = (username: string): boolean =>
+ !!usernameBlackList.length && usernameBlackList.some((restrictedUsername) => restrictedUsername.test(username.trim()));
+
+ const registerUser = useMethod('registerUser');
+
+ const { mutate: registerAdminUser, isPending } = useMutation({
+ mutationFn: async (data: AdminInfoFormData) => {
+ await registerUser({
+ name: data.name,
+ username: data.username,
+ email: data.email,
+ pass: data.password,
+ });
+ },
+ onSuccess: () => {
+ dispatchToastMessage({ type: 'success', message: t('Admin_user_created_successfully' as any) });
+ },
+ onError: (error) => {
+ dispatchToastMessage({ type: 'error', message: error });
+ },
+ });
+
+ const {
+ control,
+ handleSubmit,
+ formState: { errors },
+ watch,
+ } = useForm();
+
+ const password = watch('password');
+
+ const validateUsername = (username: string): boolean | string => {
+ if (!usernameRegExp.test(username) || hasBlockedName(username)) {
+ return t('Invalid_username');
+ }
+ return true;
+ };
+
+ const validateEmailField = (email: string): boolean | string => {
+ if (!validateEmail(email)) {
+ return t('Invalid_email');
+ }
+ return true;
+ };
+
+ const validatePassword = (password: string): boolean | string => {
+ if (!password || password.length === 0) {
+ return t('Required_field', { field: t('Password') });
+ }
+ return true;
+ };
+
+ const validateConfirmPassword = (confirmPassword: string): boolean | string => {
+ if (confirmPassword !== password) {
+ return t('Passwords_do_not_match');
+ }
+ return true;
+ };
+
+ const onSubmit = (data: AdminInfoFormData) => {
+ registerAdminUser(data);
+ };
+
+ return (
+
+
+
+
+ {t('Name')}
+
+
+ (
+
+ )}
+ />
+
+ {errors.name && (
+
+ {errors.name.message}
+
+ )}
+
+
+
+ {t('Username')}
+
+
+ (
+
+ )}
+ />
+
+ {errors.username && (
+
+ {errors.username.message}
+
+ )}
+
+
+
+ {t('Email')}
+
+
+ (
+
+ )}
+ />
+
+ {errors.email && (
+
+ {errors.email.message}
+
+ )}
+
+
+
+ {t('Password')}
+
+
+ (
+
+ )}
+ />
+
+ {errors.password && (
+
+ {errors.password.message}
+
+ )}
+
+
+
+ {t('Confirm_password')}
+
+
+ (
+
+ )}
+ />
+
+ {errors.confirmPassword && (
+
+ {errors.confirmPassword.message}
+
+ )}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AdminInfoStep;