From ba6b4c68549f139e61da01cce340313c82578146 Mon Sep 17 00:00:00 2001 From: "kasia.oczkowska" Date: Fri, 12 Jul 2024 14:48:15 +0100 Subject: [PATCH 1/3] feat: create onboarding UX flow --- .../src/components/InstallationForm/index.jsx | 208 ++++++++++++++++++ packages/web/src/config/urls.js | 1 + .../web/src/helpers/translationValues.jsx | 2 + packages/web/src/hooks/useInstallation.js | 15 ++ packages/web/src/locales/en.json | 11 + packages/web/src/pages/Installation/index.jsx | 21 ++ packages/web/src/routes.jsx | 29 ++- 7 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/components/InstallationForm/index.jsx create mode 100644 packages/web/src/hooks/useInstallation.js create mode 100644 packages/web/src/pages/Installation/index.jsx diff --git a/packages/web/src/components/InstallationForm/index.jsx b/packages/web/src/components/InstallationForm/index.jsx new file mode 100644 index 00000000..fcf543e5 --- /dev/null +++ b/packages/web/src/components/InstallationForm/index.jsx @@ -0,0 +1,208 @@ +import * as React from 'react'; +import { Link as RouterLink } from 'react-router-dom'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import { Alert } from '@mui/material'; +import LoadingButton from '@mui/lab/LoadingButton'; +import * as yup from 'yup'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { enqueueSnackbar } from 'notistack'; +import { useQueryClient } from '@tanstack/react-query'; +import Link from '@mui/material/Link'; + +import useFormatMessage from 'hooks/useFormatMessage'; +import useInstallation from 'hooks/useInstallation'; +import * as URLS from 'config/urls'; +import Form from 'components/Form'; +import TextField from 'components/TextField'; + +const validationSchema = yup.object().shape({ + fullName: yup.string().trim().required('installationForm.mandatoryInput'), + email: yup + .string() + .trim() + .email('installationForm.validateEmail') + .required('installationForm.mandatoryInput'), + password: yup.string().required('installationForm.mandatoryInput'), + confirmPassword: yup + .string() + .required('installationForm.mandatoryInput') + .oneOf([yup.ref('password')], 'installationForm.passwordsMustMatch'), +}); + +const initialValues = { + fullName: '', + email: '', + password: '', + confirmPassword: '', +}; + +function InstallationForm() { + const formatMessage = useFormatMessage(); + const install = useInstallation(); + const queryClient = useQueryClient(); + + const handleOnRedirect = () => { + queryClient.invalidateQueries({ + queryKey: ['automatisch', 'config'], + }); + }; + + const handleSubmit = async (values) => { + const { fullName, email, password } = values; + try { + await install.mutateAsync({ + fullName, + email, + password, + }); + } catch (error) { + enqueueSnackbar( + error?.message || formatMessage('installationForm.error'), + { + variant: 'error', + }, + ); + } + }; + + return ( + + theme.palette.text.disabled, + pb: 2, + mb: 2, + }} + gutterBottom + > + {formatMessage('installationForm.title')} + +
( + <> + + + + + + + {formatMessage('installationForm.submit')} + + + )} + /> + {install.isSuccess && ( + + {formatMessage('installationForm.success', { + link: (str) => ( + + {str} + + ), + })} + + )} + + ); +} + +export default InstallationForm; diff --git a/packages/web/src/config/urls.js b/packages/web/src/config/urls.js index f0180199..865edf82 100644 --- a/packages/web/src/config/urls.js +++ b/packages/web/src/config/urls.js @@ -8,6 +8,7 @@ export const SIGNUP = '/sign-up'; export const ACCEPT_INVITATON = '/accept-invitation'; export const FORGOT_PASSWORD = '/forgot-password'; export const RESET_PASSWORD = '/reset-password'; +export const INSTALLATION = '/installation'; export const APPS = '/apps'; export const NEW_APP_CONNECTION = '/apps/new'; export const APP = (appKey) => `/app/${appKey}`; diff --git a/packages/web/src/helpers/translationValues.jsx b/packages/web/src/helpers/translationValues.jsx index f5c9855c..1c608acd 100644 --- a/packages/web/src/helpers/translationValues.jsx +++ b/packages/web/src/helpers/translationValues.jsx @@ -1,10 +1,12 @@ import { Link as RouterLink } from 'react-router-dom'; import Link from '@mui/material/Link'; + export const generateInternalLink = (link) => (str) => ( {str} ); + export const generateExternalLink = (link) => (str) => ( {str} diff --git a/packages/web/src/hooks/useInstallation.js b/packages/web/src/hooks/useInstallation.js new file mode 100644 index 00000000..959f2b69 --- /dev/null +++ b/packages/web/src/hooks/useInstallation.js @@ -0,0 +1,15 @@ +import { useMutation } from '@tanstack/react-query'; + +import api from 'helpers/api'; + +export default function useInstallation() { + const mutation = useMutation({ + mutationFn: async (payload) => { + const { data } = await api.post('/v1/installation/users', payload); + + return data; + }, + }); + + return mutation; +} diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json index 26d0c233..c1efdc19 100644 --- a/packages/web/src/locales/en.json +++ b/packages/web/src/locales/en.json @@ -124,6 +124,17 @@ "webhookUrlInfo.description": "You'll need to configure your application with this webhook URL.", "webhookUrlInfo.helperText": "We've generated a custom webhook URL for you to send requests to. Learn more about webhooks.", "webhookUrlInfo.copy": "Copy", + "installationForm.title": "Installation", + "installationForm.fullNameFieldLabel": "Full name", + "installationForm.emailFieldLabel": "Email", + "installationForm.passwordFieldLabel": "Password", + "installationForm.confirmPasswordFieldLabel": "Confirm password", + "installationForm.submit": "Install", + "installationForm.validateEmail": "Email must be valid.", + "installationForm.passwordsMustMatch": "Passwords must match.", + "installationForm.mandatoryInput": "{inputName} is required.", + "installationForm.success": "The admin account has been created, and thus, the installation has been completed. You can now log in here.", + "installationForm.error": "Something went wrong. Please try again.", "signupForm.title": "Sign up", "signupForm.fullNameFieldLabel": "Full name", "signupForm.emailFieldLabel": "Email", diff --git a/packages/web/src/pages/Installation/index.jsx b/packages/web/src/pages/Installation/index.jsx new file mode 100644 index 00000000..73d68338 --- /dev/null +++ b/packages/web/src/pages/Installation/index.jsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; + +import Container from 'components/Container'; +import InstallationForm from 'components/InstallationForm'; + +export default function Installation() { + return ( + + + + + + ); +} diff --git a/packages/web/src/routes.jsx b/packages/web/src/routes.jsx index 3ed2dab0..065dfeda 100644 --- a/packages/web/src/routes.jsx +++ b/packages/web/src/routes.jsx @@ -1,4 +1,10 @@ -import { Route, Routes as ReactRouterRoutes, Navigate } from 'react-router-dom'; +import { useEffect } from 'react'; +import { + Route, + Routes as ReactRouterRoutes, + Navigate, + useNavigate, +} from 'react-router-dom'; import Layout from 'components/Layout'; import NoResultFound from 'components/NotFound'; @@ -23,12 +29,22 @@ import adminSettingsRoutes from './adminSettingsRoutes'; import Notifications from 'pages/Notifications'; import useAutomatischConfig from 'hooks/useAutomatischConfig'; import useAuthentication from 'hooks/useAuthentication'; +import Installation from 'pages/Installation'; function Routes() { const { data: configData } = useAutomatischConfig(); const { isAuthenticated } = useAuthentication(); const config = configData?.data; + const installed = configData?.data?.['installation.completed'] === true; + const navigate = useNavigate(); + + useEffect(() => { + if (!installed) { + navigate(URLS.INSTALLATION, { replace: true }); + } + }, []); + return ( + {!installed && ( + + + + } + /> + )} + {!config?.disableNotificationsPage && ( Date: Mon, 15 Jul 2024 09:00:13 +0000 Subject: [PATCH 2/3] feat(InstallationForm): use thinner font weight in success alert --- packages/web/src/components/InstallationForm/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/components/InstallationForm/index.jsx b/packages/web/src/components/InstallationForm/index.jsx index fcf543e5..04e30f3e 100644 --- a/packages/web/src/components/InstallationForm/index.jsx +++ b/packages/web/src/components/InstallationForm/index.jsx @@ -186,7 +186,7 @@ function InstallationForm() { )} /> {install.isSuccess && ( - + {formatMessage('installationForm.success', { link: (str) => ( Date: Mon, 15 Jul 2024 12:24:35 +0000 Subject: [PATCH 3/3] feat(InstallationForm): rephrase submit button --- packages/web/src/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json index c1efdc19..c1f45d5b 100644 --- a/packages/web/src/locales/en.json +++ b/packages/web/src/locales/en.json @@ -129,7 +129,7 @@ "installationForm.emailFieldLabel": "Email", "installationForm.passwordFieldLabel": "Password", "installationForm.confirmPasswordFieldLabel": "Confirm password", - "installationForm.submit": "Install", + "installationForm.submit": "Create admin", "installationForm.validateEmail": "Email must be valid.", "installationForm.passwordsMustMatch": "Passwords must match.", "installationForm.mandatoryInput": "{inputName} is required.",