import { Button, Form, Input, Space, Switch, Typography } from 'antd'; import editOrg from 'api/user/editOrg'; import getInviteDetails from 'api/user/getInviteDetails'; import loginApi from 'api/user/login'; import signUpApi from 'api/user/signup'; import afterLogin from 'AppRoutes/utils'; import WelcomeLeftContainer from 'components/WelcomeLeftContainer'; import { FeatureKeys } from 'constants/features'; import ROUTES from 'constants/routes'; import useFeatureFlag from 'hooks/useFeatureFlag'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { useLocation } from 'react-router-dom'; import { SuccessResponse } from 'types/api'; import { PayloadProps } from 'types/api/user/getUser'; import { PayloadProps as LoginPrecheckPayloadProps } from 'types/api/user/loginPrecheck'; import { trackEvent } from 'utils/segmentAnalytics'; import { ButtonContainer, FormContainer, FormWrapper, Label, MarginTop, } from './styles'; import { isPasswordNotValidMessage, isPasswordValid } from './utils'; const { Title } = Typography; type FormValues = { firstName: string; email: string; organizationName: string; password: string; confirmPassword: string; hasOptedUpdates: boolean; isAnonymous: boolean; }; function SignUp({ version }: SignUpProps): JSX.Element { const { t } = useTranslation(['signup']); const [loading, setLoading] = useState(false); const [precheck, setPrecheck] = useState({ sso: false, isUser: false, }); const [confirmPasswordError, setConfirmPasswordError] = useState( false, ); const [isPasswordPolicyError, setIsPasswordPolicyError] = useState( false, ); const { search } = useLocation(); const params = new URLSearchParams(search); const token = params.get('token'); const [isDetailsDisable, setIsDetailsDisable] = useState(false); const isOnboardingEnabled = useFeatureFlag(FeatureKeys.ONBOARDING)?.active; const getInviteDetailsResponse = useQuery({ queryFn: () => getInviteDetails({ inviteId: token || '', }), queryKey: ['getInviteDetails', token], enabled: token !== null, }); const { notifications } = useNotifications(); const [form] = Form.useForm(); useEffect(() => { if ( getInviteDetailsResponse.status === 'success' && getInviteDetailsResponse.data.payload ) { const responseDetails = getInviteDetailsResponse.data.payload; if (responseDetails.precheck) setPrecheck(responseDetails.precheck); form.setFieldValue('firstName', responseDetails.name); form.setFieldValue('email', responseDetails.email); form.setFieldValue('organizationName', responseDetails.organization); setIsDetailsDisable(true); trackEvent('Account Creation Page Visited', { email: responseDetails.email, name: responseDetails.name, company_name: responseDetails.organization, source: 'SigNoz Cloud', }); } }, [ getInviteDetailsResponse.data?.payload, form, getInviteDetailsResponse.status, ]); useEffect(() => { if ( getInviteDetailsResponse.status === 'success' && getInviteDetailsResponse.data?.error ) { const { error } = getInviteDetailsResponse.data; notifications.error({ message: error, }); } }, [ getInviteDetailsResponse.data, getInviteDetailsResponse.status, notifications, ]); const isPreferenceVisible = token === null; const commonHandler = async ( values: FormValues, callback: ( e: SuccessResponse, values: FormValues, ) => Promise | VoidFunction, ): Promise => { try { const { organizationName, password, firstName, email } = values; const response = await signUpApi({ email, name: firstName, orgName: organizationName, password, token: params.get('token') || undefined, }); if (response.statusCode === 200) { const loginResponse = await loginApi({ email, password, }); if (loginResponse.statusCode === 200) { const { payload } = loginResponse; const userResponse = await afterLogin( payload.userId, payload.accessJwt, payload.refreshJwt, ); if (userResponse) { callback(userResponse, values); } } else { notifications.error({ message: loginResponse.error || t('unexpected_error'), }); } } else { notifications.error({ message: response.error || t('unexpected_error'), }); } } catch (error) { notifications.error({ message: t('unexpected_error'), }); } }; const onAdminAfterLogin = async ( userResponse: SuccessResponse, values: FormValues, ): Promise => { const editResponse = await editOrg({ isAnonymous: values.isAnonymous, name: values.organizationName, hasOptedUpdates: values.hasOptedUpdates, orgId: userResponse.payload.orgId, }); if (editResponse.statusCode === 200) { history.push(ROUTES.APPLICATION); } else { notifications.error({ message: editResponse.error || t('unexpected_error'), }); } }; const handleSubmitSSO = async (): Promise => { if (!params.get('token')) { notifications.error({ message: t('token_required'), }); return; } setLoading(true); try { const values = form.getFieldsValue(); const response = await signUpApi({ email: values.email, name: values.firstName, orgName: values.organizationName, password: values.password, token: params.get('token') || undefined, sourceUrl: encodeURIComponent(window.location.href), }); if (response.statusCode === 200) { if (response.payload?.sso) { if (response.payload?.ssoUrl) { window.location.href = response.payload?.ssoUrl; } else { notifications.error({ message: t('failed_to_initiate_login'), }); // take user to login page as there is nothing to do here history.push(ROUTES.LOGIN); } } } else { notifications.error({ message: response.error || t('unexpected_error'), }); } } catch (error) { notifications.error({ message: t('unexpected_error'), }); } setLoading(false); }; const handleSubmit = (): void => { (async (): Promise => { const { hostname } = window.location; try { const values = form.getFieldsValue(); setLoading(true); if (!isPasswordValid(values.password)) { trackEvent('Account Creation Page - Invalid Password', { email: values.email, name: values.firstName, }); setIsPasswordPolicyError(true); setLoading(false); return; } if (isPreferenceVisible) { await commonHandler(values, onAdminAfterLogin); } else { trackEvent('Account Created Successfully', { email: values.email, name: values.firstName, }); await commonHandler( values, async (): Promise => { if ( isOnboardingEnabled && hostname && hostname.endsWith('signoz.cloud') ) { history.push(ROUTES.GET_STARTED); } else { history.push(ROUTES.APPLICATION); } }, ); } setLoading(false); } catch (error) { notifications.error({ message: t('unexpected_error'), }); setLoading(false); } })(); }; const getIsNameVisible = (): boolean => !(form.getFieldValue('firstName') === 0 && !isPreferenceVisible); const isNameVisible = getIsNameVisible(); const handleValuesChange: (changedValues: Partial) => void = ( changedValues, ) => { if ('password' in changedValues || 'confirmPassword' in changedValues) { const { password, confirmPassword } = form.getFieldsValue(); const isInvalidPassword = !isPasswordValid(password) && password.length > 0; setIsPasswordPolicyError(isInvalidPassword); const isSamePassword = password === confirmPassword; setConfirmPasswordError(!isSamePassword); } }; const isValidForm: () => boolean = () => { const values = form.getFieldsValue(); return ( loading || !values.email || !values.organizationName || (!precheck.sso && (!values.password || !values.confirmPassword)) || (!isDetailsDisable && !values.firstName) || confirmPasswordError || isPasswordPolicyError ); }; return ( Create your account
{isNameVisible && (
{' '}
)}
{' '}
{!precheck.sso && (
{' '}
)} {!precheck.sso && (
{' '} {confirmPasswordError && ( {t('failed_confirm_password')} )} {isPasswordPolicyError && ( {isPasswordNotValidMessage} )}
)} {isPreferenceVisible && ( <> {t('prompt_keepme_posted')} {t('prompt_anonymise')} )} {isPreferenceVisible && ( This will create an admin account. If you are not an admin, please ask your admin for an invite link )}
); } interface SignUpProps { version: string; } export default SignUp;