diff --git a/src/features/auth/syncWordpress.ts b/src/features/auth/syncWordpress.ts index 45cb0861e..e5a778e7c 100644 --- a/src/features/auth/syncWordpress.ts +++ b/src/features/auth/syncWordpress.ts @@ -11,20 +11,25 @@ export const syncWordpress = async (): Promise => { console.error('SyncWP: No ID token available'); return; } + try { + const response = await fetch( + `${process.env.REACT_APP_CROWD_WP_URL}/wp-json/cognito-sync/v1/login`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + credentials: 'include', + body: JSON.stringify({ id_token: idToken }), + } + ); - const response = await fetch( - `${process.env.REACT_APP_CROWD_WP_URL}/wp-json/cognito-sync/v1/login`, - { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - credentials: 'include', - body: JSON.stringify({ id_token: idToken }), - } - ); + const result = await response.json(); - const result = await response.json(); - if (isDev()) { + if (isDev()) { + // eslint-disable-next-line no-console + console.log('🚀 ~ syncWordpress ~ result:', result); + } + } catch (error: any) { // eslint-disable-next-line no-console - console.log('🚀 ~ syncWordpress ~ result:', result); + console.error('SyncWP error:', error); } }; diff --git a/src/pages/JoinPage/InvitedUserPage/SetPasswordForm.tsx b/src/pages/JoinPage/InvitedUserPage/SetPasswordForm.tsx index 786bc5643..9f0fbe5a1 100644 --- a/src/pages/JoinPage/InvitedUserPage/SetPasswordForm.tsx +++ b/src/pages/JoinPage/InvitedUserPage/SetPasswordForm.tsx @@ -14,7 +14,7 @@ import { ReactComponent as EyeHide } from '@zendeskgarden/svg-icons/src/16/eye-h import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import { appTheme } from 'src/app/theme'; import { PasswordRequirements } from 'src/common/components/PasswordRequirements'; import { useAuth } from 'src/features/auth/context'; @@ -48,6 +48,7 @@ export const SetPasswordForm = ({ const { t } = useTranslation(); const { signup, login, setNewPassword } = useAuth(); const navigate = useNavigate(); + const [searchParams] = useSearchParams(); const sendGTMevent = useSendGTMevent({ loggedUser: false }); const [passwordInputType, setPasswordInputType] = useState('password'); const [confirmPasswordInputType, setConfirmPasswordInputType] = @@ -98,7 +99,10 @@ export const SetPasswordForm = ({ // 3. L'utente è ora loggato, salva i dati dell'invito e vai all'onboarding sessionStorage.setItem('inviteProfileId', profileId.toString()); sessionStorage.setItem('inviteToken', token); - navigate('/join/onboarding'); + + // Preserva i query params durante la navigazione + const queryString = searchParams.toString(); + navigate(`/join/onboarding${queryString ? `?${queryString}` : ''}`); return; } @@ -125,8 +129,9 @@ export const SetPasswordForm = ({ sessionStorage.setItem('inviteProfileId', profileId.toString()); sessionStorage.setItem('inviteToken', token); - // Redirect all'onboarding già loggato - navigate('/join/onboarding'); + // Preserva i query params durante la navigazione + const queryString = searchParams.toString(); + navigate(`/join/onboarding${queryString ? `?${queryString}` : ''}`); } catch (loginError: any) { // Se il login fallisce perché serve conferma email if (loginError.message?.includes('User is not confirmed')) { diff --git a/src/pages/JoinPage/OnboardingPage/OnboardingProvider.tsx b/src/pages/JoinPage/OnboardingPage/OnboardingProvider.tsx index 5d2cbf3a9..e9de138cd 100644 --- a/src/pages/JoinPage/OnboardingPage/OnboardingProvider.tsx +++ b/src/pages/JoinPage/OnboardingPage/OnboardingProvider.tsx @@ -28,10 +28,16 @@ export interface OnboardingUserData { type: 'new' | 'invite'; } +export interface QueryParams { + template?: number; + [key: string]: string | number | undefined; +} + interface OnboardingContextType { step: number; data: OnboardingData; userData: OnboardingUserData; + queryParams: QueryParams; setStep: (step: number) => void; updateData: (data: Partial) => void; } @@ -40,11 +46,13 @@ const OnboardingContext = createContext(null); interface OnboardingProviderProps { userData: OnboardingUserData; + queryParams?: QueryParams; children: (context: OnboardingContextType) => ReactNode; } export const OnboardingProvider = ({ userData, + queryParams = {}, children, }: OnboardingProviderProps) => { const [step, setStep] = useState(1); @@ -90,9 +98,10 @@ export const OnboardingProvider = ({ setStep, data, userData, + queryParams, updateData, }), - [step, data, userData] + [step, data, userData, queryParams] ); return ( diff --git a/src/pages/JoinPage/OnboardingPage/Steps/PersonalInfoStep.tsx b/src/pages/JoinPage/OnboardingPage/Steps/PersonalInfoStep.tsx index d8599ba40..8806e6dc6 100644 --- a/src/pages/JoinPage/OnboardingPage/Steps/PersonalInfoStep.tsx +++ b/src/pages/JoinPage/OnboardingPage/Steps/PersonalInfoStep.tsx @@ -19,7 +19,7 @@ import { } from 'formik'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate, useSearchParams } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import { appTheme } from 'src/app/theme'; import { useGetUsersRolesQuery, @@ -116,9 +116,8 @@ const AutofillSync = () => { export const PersonalInfoStep = () => { const { t } = useTranslation(); - const { data, userData, updateData, setStep } = useOnboarding(); + const { data, userData, updateData, setStep, queryParams } = useOnboarding(); const navigate = useNavigate(); - const [searchParams] = useSearchParams(); const sendGTMevent = useSendGTMevent({ loggedUser: false }); const [postUsers] = usePostUsersMutation(); const { data: dataRoles, isLoading: isLoadingRoles } = @@ -128,15 +127,8 @@ export const PersonalInfoStep = () => { const selectRef = useRef(null); const roleSelectRef = useRef(null); - const templateParam = searchParams.get('template'); - let templateId: number | undefined; - - if (templateParam !== null) { - const parsed = Number(templateParam); - if (Number.isInteger(parsed)) { - templateId = parsed; - } - } + // Usa il templateId dai queryParams del provider + const templateId = queryParams.template; const renderRolesOptions = useMemo( () => diff --git a/src/pages/JoinPage/OnboardingPage/Steps/WorkspaceStep.tsx b/src/pages/JoinPage/OnboardingPage/Steps/WorkspaceStep.tsx index 2ba3d6007..ac6251df1 100644 --- a/src/pages/JoinPage/OnboardingPage/Steps/WorkspaceStep.tsx +++ b/src/pages/JoinPage/OnboardingPage/Steps/WorkspaceStep.tsx @@ -10,7 +10,7 @@ import { } from '@appquality/unguess-design-system'; import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik'; import { useTranslation } from 'react-i18next'; -import { useNavigate, useSearchParams } from 'react-router-dom'; + import { appTheme } from 'src/app/theme'; import { usePostUsersMutation } from 'src/features/api'; import { useSendGTMevent } from 'src/hooks/useGTMevent'; @@ -18,6 +18,7 @@ import styled from 'styled-components'; import { AuthStepWrapper } from 'src/common/components/AuthCardWrapper'; import { useOnboarding } from '../OnboardingProvider'; import { getWorkspaceValidationSchema } from '../validationSchema'; +import { sendToHubspot } from '../../sendToHubspot'; const FieldContainer = styled.div` display: flex; @@ -39,21 +40,11 @@ interface WorkspaceFormValues { export const WorkspaceStep = () => { const { t } = useTranslation(); - const { data, userData, updateData, setStep } = useOnboarding(); - const navigate = useNavigate(); - const [searchParams] = useSearchParams(); + const { data, userData, updateData, setStep, queryParams } = useOnboarding(); const sendGTMevent = useSendGTMevent({ loggedUser: false }); const [postUsers] = usePostUsersMutation(); - - const templateParam = searchParams.get('template'); - let templateId: number | undefined; - - if (templateParam !== null) { - const parsed = Number(templateParam); - if (Number.isInteger(parsed)) { - templateId = parsed; - } - } + const templateId = queryParams.template; + const redirectTo = queryParams.redirect as string | undefined; const handleSubmit = async ( values: WorkspaceFormValues, @@ -104,6 +95,17 @@ export const WorkspaceStep = () => { content: res.projectId ? 'with project' : 'without project', }); + try { + await sendToHubspot({ + email: userData.email!, + firstName: data.name, + lastName: data.surname, + searchParams: queryParams, + }); + } catch (err) { + console.error('Error sending data to HubSpot:', err); + } + // Pulisci sessionStorage per utenti invitati if (userData.type === 'invite') { sessionStorage.removeItem('inviteProfileId'); @@ -111,11 +113,8 @@ export const WorkspaceStep = () => { } // Redirect appropriato (utente già loggato) - if (res.projectId) { - navigate(`/projects/${res.projectId}`); - } else { - navigate('/'); - } + const redirectTarget = res.projectId ? `/projects/${res.projectId}` : '/'; + window.location.href = redirectTo || redirectTarget; } catch (error: any) { // eslint-disable-next-line no-console console.error('Onboarding save error:', error); diff --git a/src/pages/JoinPage/OnboardingPage/index.tsx b/src/pages/JoinPage/OnboardingPage/index.tsx index eb44fe8f2..bfb757c42 100644 --- a/src/pages/JoinPage/OnboardingPage/index.tsx +++ b/src/pages/JoinPage/OnboardingPage/index.tsx @@ -4,6 +4,7 @@ * Step 2: Nome workspace */ import { useTranslation } from 'react-i18next'; +import { useSearchParams } from 'react-router-dom'; import { LayoutWrapper } from 'src/common/components/LayoutWrapper'; import { Track } from 'src/common/Track'; import { useGetUsersMeQuery } from 'src/features/api'; @@ -15,7 +16,11 @@ import styled from 'styled-components'; import { AuthHeader } from '../../LoginPage/parts/AuthHeader'; import { AuthFooter } from '../../LoginPage/parts/AuthFooter'; import { ImagesColumn } from '../ImagesColumn'; -import { OnboardingProvider, OnboardingUserData } from './OnboardingProvider'; +import { + OnboardingProvider, + OnboardingUserData, + QueryParams, +} from './OnboardingProvider'; import { PersonalInfoStep } from './Steps/PersonalInfoStep'; import { WorkspaceStep } from './Steps/WorkspaceStep'; @@ -80,6 +85,24 @@ const RightColumn = styled.div` const OnboardingPage = () => { const { t } = useTranslation(); const { data: currentUser, isLoading } = useGetUsersMeQuery(); + const [searchParams] = useSearchParams(); + + // Estrai i parametri query string + const queryParams: QueryParams = {}; + const templateParam = searchParams.get('template'); + if (templateParam !== null) { + const parsed = Number(templateParam); + if (Number.isInteger(parsed)) { + queryParams.template = parsed; + } + } + + // Copia tutti gli altri parametri (utm_source, utm_medium, utm_campaign, etc.) + searchParams.forEach((value, key) => { + if (key !== 'template') { + queryParams[key] = value; + } + }); // Costruisci userData da useGetUsersMeQuery let userData: OnboardingUserData | undefined; @@ -120,7 +143,7 @@ const OnboardingPage = () => { description={t('__PAGE_ONBOARDING_DESCRIPTION')} metaTags={meta} > - + {({ step }) => ( diff --git a/src/pages/JoinPage/RouteGuards.tsx b/src/pages/JoinPage/RouteGuards.tsx index e5a57c467..4e39d90cb 100644 --- a/src/pages/JoinPage/RouteGuards.tsx +++ b/src/pages/JoinPage/RouteGuards.tsx @@ -2,12 +2,13 @@ * RouteGuards - Componenti per proteggere le route di JoinPage */ import { ReactNode } from 'react'; -import { Navigate } from 'react-router-dom'; +import { Navigate, useSearchParams } from 'react-router-dom'; import { PageLoader } from 'src/common/components/PageLoader'; import { useGetUsersMeQuery } from 'src/features/api'; export const PublicRoute = ({ children }: { children: ReactNode }) => { const { data: userData, isLoading } = useGetUsersMeQuery(); + const [searchParams] = useSearchParams(); if (isLoading) { return ; @@ -17,7 +18,13 @@ export const PublicRoute = ({ children }: { children: ReactNode }) => { if (userData) { // Se onboarding è pending if (userData.onboarding_pending) { - return ; + const queryString = searchParams.toString(); + return ( + + ); } // Se l'utente ha completato l'onboarding @@ -33,6 +40,7 @@ export const PublicRoute = ({ children }: { children: ReactNode }) => { */ export const OnboardingRoute = ({ children }: { children: ReactNode }) => { const { data: userData, isLoading, error } = useGetUsersMeQuery(); + const [searchParams] = useSearchParams(); if (isLoading) { return ; @@ -40,7 +48,13 @@ export const OnboardingRoute = ({ children }: { children: ReactNode }) => { // Se non c'è utente autenticato o c'è un errore (es. 401, 403) if (!userData || error) { - return ; + const queryString = searchParams.toString(); + return ( + + ); } // Se onboarding NON è pending, redirect alla home diff --git a/src/pages/JoinPage/SignupPage/ConfirmEmailForm.tsx b/src/pages/JoinPage/SignupPage/ConfirmEmailForm.tsx index d367c760c..19209cf44 100644 --- a/src/pages/JoinPage/SignupPage/ConfirmEmailForm.tsx +++ b/src/pages/JoinPage/SignupPage/ConfirmEmailForm.tsx @@ -13,7 +13,7 @@ import { } from '@appquality/unguess-design-system'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import { appTheme } from 'src/app/theme'; import { useAuth } from 'src/features/auth/context'; import { useSendGTMevent } from 'src/hooks/useGTMevent'; @@ -31,6 +31,7 @@ export const ConfirmEmailForm = ({ const { t } = useTranslation(); const { confirmSignup, login, resendSignupCode } = useAuth(); const navigate = useNavigate(); + const [searchParams] = useSearchParams(); const sendGTMevent = useSendGTMevent({ loggedUser: false }); const [code, setCode] = useState(''); @@ -70,7 +71,8 @@ export const ConfirmEmailForm = ({ action: 'auto-login completed', }); - navigate('/join/onboarding'); + const queryString = searchParams.toString(); + navigate(`/join/onboarding${queryString ? `?${queryString}` : ''}`); } catch (err: any) { // eslint-disable-next-line no-console console.error('Confirmation error:', err); diff --git a/src/pages/JoinPage/sendToHubspot.ts b/src/pages/JoinPage/sendToHubspot.ts index 733a45f65..33b66f6a7 100644 --- a/src/pages/JoinPage/sendToHubspot.ts +++ b/src/pages/JoinPage/sendToHubspot.ts @@ -7,6 +7,7 @@ export async function sendToHubspot(data: { email: string; firstName: string; lastName: string; + searchParams: Record; }) { if (process.env.NODE_ENV === 'test') return false; // Skip actual API call during tests @@ -21,14 +22,12 @@ export async function sendToHubspot(data: { const pageName = document.title; - const params = new URLSearchParams(window.location.search); - const utm = { - source: params.get('utm_source'), - medium: params.get('utm_medium'), - campaign: params.get('utm_campaign'), - term: params.get('utm_term'), - content: params.get('utm_content'), + source: data.searchParams.utm_source, + medium: data.searchParams.utm_medium, + campaign: data.searchParams.utm_campaign, + term: data.searchParams.utm_term, + content: data.searchParams.utm_content, }; const payload = {