Closed thealdoamati closed 3 months ago
This is the page that I tried to use it
import { useState, useEffect } from 'react'
import { useRouter } from 'next/router'
import IntegrationsLayout from '../../../../components/Integrations/IntegrationsLayout'
import ManualIntegration from '../../../../components/Integrations/Manual/ManualIntegration'
import AllIntegration from '../../../../components/Integrations/All/AllIntegration'
import SourceIntegration from '../../../../components/Integrations/Sources/SourcesIntegration'
import CadastroCustos from '../../../../components/Integrations/Custos/CadastroCustos'
import ConfigsIntegrations from '../../../../components/Integrations/Configs/ConfigsIntegration'
import StepContextProvider from '../../../../contexts/StepContext'
import { JoyrideProgressProvider } from '../../../../contexts/JoyrideContext'
export default function Integrations() {
const router = useRouter()
const [step, setStep] = useState<number>(1)
useEffect(() => {
if (router.query.step) {
const stepFromUrl = Number(router.query.step)
if (!isNaN(stepFromUrl)) {
setStep(stepFromUrl)
}
}
}, [router.query.step])
function handleStep(step: number) {
switch (step) {
case 1:
return <AllIntegration setStep={setStep} />
case 2:
return <ManualIntegration />
case 3:
return <SourceIntegration setStep={setStep} />
case 4:
return <CadastroCustos />
case 5:
return <ConfigsIntegrations />
}
}
return (
<IntegrationsLayout setStep={setStep} step={step}>
<StepContextProvider>
<JoyrideProgressProvider>{handleStep(step)}</JoyrideProgressProvider>
</StepContextProvider>
</IntegrationsLayout>
)
}
I created a context to work in 4 different components on this page
import React, { createContext, useContext, useState } from 'react'
import { api } from '../services/api'
import { parseCookies } from 'nookies'
// Definindo o tipo para o contexto do Joyride// Definindo o tipo para o contexto do Joyride
interface JoyrideProgressContextType {
joyrideProgress: string
updateProgress: (componentId: string, newStep: number) => void
}
// Criando o contexto com um valor padrão
const JoyrideProgressContext = createContext<JoyrideProgressContextType>({
joyrideProgress: '11213141',
updateProgress: (componentId, newStep) => {
console.warn(
`UpdateProgress called with componentId: ${componentId} and newStep: ${newStep}`,
)
},
})
export const JoyrideProgressProvider = ({ children }: any) => {
const [joyrideProgress, setJoyrideProgress] = useState('11213141')
const updateProgress = async (componentId: string, newStep: number) => {
const index = parseInt(componentId, 10) - 1 // Convert to zero-based index
const progressArray = joyrideProgress.split('').map(Number)
// Update the step at the correct index
progressArray[index] = newStep
// Convert the array back to a string and then to a number
const updatedProgress = parseInt(progressArray.join(''), 10)
// Update local state
setJoyrideProgress(updatedProgress.toString())
// Prepare the request
const { userSessionToken } = parseCookies()
const config = {
method: 'put',
headers: {
data: { step: updatedProgress }, // Send the updated number as an integer
}
// Perform the request
try {
const response = await api(config)
console.log(`Onboarding step updated successfully:`, response.data)
} catch (error) {
console.error('Error updating onboarding step:', error)
}
}
return (
<JoyrideProgressContext.Provider
value={{ joyrideProgress, updateProgress }}
>
{children}
</JoyrideProgressContext.Provider>
)
}
export const useJoyrideProgress = () => useContext(JoyrideProgressContext)
This is one of the components that I tried to use and it's bugged on production
import { Dispatch, SetStateAction, useEffect, useState } from 'react'
import { parseCookies, destroyCookie } from 'nookies'
import { useRouter } from 'next/router'
import { toast } from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'
import AsaasLogo from '../../../assets/integracoes/AsaasLogo.svg'
import PagarmeLogo from '../../../assets/integracoes/PagarmeLogo.svg'
import IuguLogo from '../../../assets/integracoes/IuguLogo.svg'
import VindiLogo from '../../../assets/integracoes/VindiLogo.svg'
import SuperlogicaLogo from '../../../assets/integracoes/SuperlogicaLogo.svg'
import SheetsLogo from '../../../assets/integracoes/SheetsLogo.svg'
import Stripe from '../../../assets/integracoes/Stripe.svg'
import Engrenagem from '../../../assets/integracoes/engrenagem.svg'
import { formatDistanceToNow, parseISO, isBefore, subDays } from 'date-fns'
import pt from 'date-fns/locale/pt'
import { api } from '../../../services/api'
import { BasicTable } from '../../Utils/Table/BasicTable/BasicTable'
import ReactJoyride from 'react-joyride'
import { allIntegrationsSteps } from '../../../utils/steps/stepsJoyride'
import { useJoyrideProgress } from '../../../contexts/JoyrideContext'
interface Props {
setStep: Dispatch<SetStateAction<number>>
}
const SUCCESS_MSG = 'Conexão excluída com sucesso!'
const ERROR_MSG = 'Deu ruim'
export default function AllIntegration({ setStep }: Props) {
const [isPageAllowed, setIsPageAllowed] = useState<boolean>(false)
const [connections, setConnections] = useState<any>([])
const [isJoyrideRunning, setIsJoyrideRunning] = useState(false)
const { joyrideProgress, updateProgress } = useJoyrideProgress()
const [loadingConnections, setLoadingConnections] = useState<{
[key: string]: boolean
}>({})
const { push } = useRouter()
const logoMapping = {
asaas: AsaasLogo,
stripe: Stripe,
pagarme: PagarmeLogo,
iugu: IuguLogo,
vindi: VindiLogo,
superlogica: SuperlogicaLogo,
googleSheets: SheetsLogo,
}
async function getUserData() {
const { userAddress, userSessionToken } = parseCookies() // Obtém o endereço do usuário e o token da sessão do usuário a partir dos cookies.
if (userSessionToken) {
const body = JSON.stringify({
address: userAddress,
})
const config = {
method: 'post',
url: '/getCurrentUser',
data: body,
}
await api(config)
.then((res) => {
setIsPageAllowed(true)
})
.catch((err) => {
console.log('vou puxarrr1')
localStorage.removeItem('@scalable: user-state-1.0.0')
localStorage.clear()
destroyCookie(undefined, 'userAddress')
destroyCookie(undefined, 'userSessionToken')
console.log(err)
push('/')
})
}
}
console.log('a')
useEffect(() => {
getUserData()
const { userSessionToken } = parseCookies()
const config = {
method: 'get',
api(config)
.then((response) => {
if (response && response.data) {
const updatedConnections = response.data.map((connection: any) => {
const date = parseISO(connection.atualizadoEm)
const timeAgo = formatDistanceToNow(date, {
addSuffix: true,
locale: pt,
})
const isOutdated = isBefore(date, subDays(new Date(), 1))
return {
...connection,
logo:
logoMapping[
connection.type as keyof {
asaas: any
stripe: any
pagarme: any
iugu: any
vindi: any
superlogica: any
googleSheets: any
}
] || null,
imgConfig: Engrenagem,
atualizadoEm: timeAgo,
isOutdated,
}
})
setConnections(updatedConnections)
} else {
toast.error('Dados nulos do backEnd')
}
})
.catch(() => {
toast.error('Erro! Tente novamente')
})
}, [])
function generateApiConfig(method: string, url: string, data?: any) {
const { userSessionToken } = parseCookies()
return {
method,
url,
const updateConnectionMappings: { [key: string]: string } = {
asaas: '/updateConnectionAsaas',
pagarme: '/updateConnectionPagarme',
superlogica: '/updateConnectionSuperlogica',
stripe: '/updateConnectionStripe',
iugu: '/updateConnectionIugu',
vindi: '/updateConnectionVindi',
googleSheets: '/updateConnectionGoogleSheets',
}
const handleUpdateConnection = (
connectionId: string | number,
connectionType: string,
) => {
const url = updateConnectionMappings[connectionType]
if (!url) {
toast.error('Tipo de conexão não reconhecido.')
return
}
toast.info(
'Essa atualização pode levar até 20 minutos, mas você pode voltar depois de concluída!',
)
setLoadingConnections((prev) => ({ ...prev, [connectionId]: true })) // Start loading
const config = generateApiConfig('post', url, { id: connectionId })
api(config)
.then(() => {
setLoadingConnections((prev) => ({ ...prev, [connectionId]: false })) // Stop loading
toast.success('Conexão atualizada com sucesso!')
})
.catch((err) => {
setLoadingConnections((prev) => ({ ...prev, [connectionId]: false })) // Stop loading
toast.error('Erro ao atualizar a conexão', err)
})
}
const deleteConnectionMappings: { [key: string]: string } = {
asaas: '/deleteConnectionAsaas',
pagarme: '/deleteConnectionPagarme',
stripe: '/deleteConnectionStripe',
iugu: '/deleteConnectionIugu',
vindi: '/deleteConnectionVindi',
superlogica: '/deleteConnectionSuperlogica',
googleSheets: '/deleteConnectionGoogleSheets',
}
const handleDeleteConnection = (
connectionId: string | number,
connectionType: string,
) => {
const url = deleteConnectionMappings[connectionType]
if (!url) {
toast.error('Tipo de conexão não reconhecido.')
return
}
const config = generateApiConfig('post', url, { id: connectionId })
api(config)
.then(() => {
// Remove a conexão da lista local
const updatedConnections = connections.filter(
(conn: any) => conn.id !== connectionId,
)
setConnections(updatedConnections)
toast.success(SUCCESS_MSG)
})
.catch((err) => {
toast.error(ERROR_MSG, err)
})
}
async function getOnboardingStepFromUser() {
const { userAddress, userSessionToken } = parseCookies()
const body = JSON.stringify({ address: userAddress })
const config = {
method: 'post',
url: '/getCurrentUser',
data: body,
}
try {
const response = await api(config)
let onboardingStep = response.data.onBoardingStepsSaas
// Garantindo que onboardingStep seja uma string
onboardingStep = onboardingStep.toString()
console.log('Current onboarding step from API:', onboardingStep)
const secondDigit = parseInt(onboardingStep.charAt(1), 10) || 0
console.log('Second digit from onboarding step:', secondDigit)
return secondDigit
} catch (error) {
console.error('Failed to get onboarding step:', error)
return 0
}
}
// Use o isRunning baseado no estado do contexto, não no estado local
const componentId = '2' // Identificador do componente, ajuste conforme necessário
const currentStep = parseInt(joyrideProgress[parseInt(componentId) - 1])
const isRunning = currentStep < 2
function finishOnBoarding() {
console.log('finishOnBoarding called')
// Passa os dois argumentos separadamente.
updateProgress('2', 2) // Aqui estamos assumindo que '2' é o componentId.
}
useEffect(() => {
console.log('isRunning changed:', isRunning)
}, [isRunning])
useEffect(() => {
async function checkOnboardingStep() {
const step = await getOnboardingStepFromUser()
const shouldRunJoyride = step !== 2 // Execute o Joyride somente se o segundo dígito não for 2
setIsJoyrideRunning(shouldRunJoyride)
console.log('Step obtained from getOnboardingStepFromUser:', step)
console.log('isJoyrideRunning set to:', shouldRunJoyride)
}
checkOnboardingStep()
}, [])
if (!isPageAllowed) {
return <div></div>
}
return (
<>
<div id="joyride-welcome" className="flex flex-col w-full gap-5">
<h1 className="text-[#4b4b4b] text-[24px] font-semibold flex items-center justify-start">
Fonte de dados
</h1>
<div className="md:flex grid flex-row items-center gap-5">
<p className="text-[#000000] text-[16px] font-semibold">
Suas fontes de dados
</p>
<button
className="bg-[rgba(0,41,255,0.80)] font-semibold rounded-[13px] text-[13px] py-[0.65rem] px-4 text-white hover:bg-blue500 transition duration-500"
onClick={() => setStep(3)}
>
+ Adicionar fonte
</button>
</div>
<div id="joyride-docs" className="mt-[20px] md:mt-0">
<BasicTable
connections={connections}
onDelete={handleDeleteConnection}
onUpdate={handleUpdateConnection}
setStep={setStep}
loadingConnections={loadingConnections}
/>
</div>
</div>
<ReactJoyride
continuous
hideCloseButton
scrollToFirstStep
showProgress
showSkipButton
run={isJoyrideRunning}
steps={allIntegrationsSteps}
callback={({ status }) => {
console.log('ReactJoyride status:', status)
if (status === 'finished' || status === 'skipped') {
finishOnBoarding()
}
}}
styles={{
buttonNext: {
backgroundColor: '#193EFF',
},
buttonBack: {
backgroundColor: 'transparent',
color: '#193EFF',
},
}}
/>
</>
)
}
Do you know what can be? @gilbarbara
This is what I see on my inspect elements, the modal is going to the bottom of the page:
🐛 Bug Report
I'm having a problem when I upload my project to production, when I click outside the joyride, in my dev branch It has the red circle
When I click outside the joyride in production, it get like this
It's like I pass to the next step, it loads without the content
It's not even showing the red dot like my dev branch
![image](https://github.com/gilbarbara/react-joyride/assets/112902097/1a31d2de-5e56-41e3-9a67-be102d6738c8)
This are my steps, the only one that works is the first one with the placement center
I have 4 components on the same page, each one using its own steps with a context for the joyride. I save the steps on my backend and then I push back in my getCurrentUser endpoint.
Anyone has already passed through this?