Can't redirect after action taken

Im losing my marbles on this one. I'm using
useHistory
useHistory
from react-router-dom to try to redirect to 1 of 2 pages. But i can't get the redirect to work for the LIFE of me. My log response payload is:
[ Server ] Response payload: {
[ Server ] message: 'Webhook received and processed',
[ Server ] gptResponse: '2',
[ Server ] redirectUrl: '/not-a-good-fit'
[ Server ] }
[ Server ] Response payload: {
[ Server ] message: 'Webhook received and processed',
[ Server ] gptResponse: '2',
[ Server ] redirectUrl: '/not-a-good-fit'
[ Server ] }
Which is great that shows redirectUrl is being chosen correctly and gpt4o is giving me the ideal response. So why no redirect :sadboi: , i would use
useNavigate
useNavigate
but thats in new router-dom.
import React, { useEffect, useRef, useState } from 'react';
import { useHistory } from 'react-router-dom';

declare global {
interface Window {
Tally: any;
}
}

const ApplicationForm: React.FC = () => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [isLoading, setIsLoading] = useState(false);
const history = useHistory();

useEffect(() => {
const script = document.createElement('script');
script.src = 'https://tally.so/widgets/embed.js';
script.async = true;
script.onload = () => {
if (window.Tally) {
window.Tally.loadEmbeds();
} else {
document.querySelectorAll("iframe[data-tally-src]:not([src])").forEach((iframe) => {
iframe.setAttribute('src', iframe.getAttribute('data-tally-src')!);
});
}
};
script.onerror = () => {
document.querySelectorAll("iframe[data-tally-src]:not([src])").forEach((iframe) => {
iframe.setAttribute('src', iframe.getAttribute('data-tally-src')!);
});
};

document.body.appendChild(script);

window.addEventListener('message', handleFormSubmit);

return () => {
document.body.removeChild(script);
window.removeEventListener('message', handleFormSubmit);
};
}, []);

const handleFormSubmit = async (event: MessageEvent) => {
console.log('Received message event:', event);
console.log('Event data:', event.data);
console.log('Event origin:', event.origin);
console.log('Event type:', event.type);

if (event.origin === 'https://tally.so' && event.data?.event === 'Tally.FormSubmitted') {
const formData = event.data.payload;
console.log('Form data received:', formData);

setIsLoading(true);

try {
const response = await fetch('https://b168-99-250-72-13.ngrok-free.app/webhook/callback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});

console.log('Response received:', response);

if (!response.ok) {
throw new Error('Error calling webhook');
}

const data = await response.json();
console.log('Response data:', data);

const gptResponse = data.gptResponse;
console.log('GPT-4 Response:', gptResponse);

const redirectUrl = data.redirectUrl;
console.log('Redirect URL:', redirectUrl);

if (redirectUrl) {
console.log('Redirecting to', redirectUrl);
history.push(redirectUrl);
} else {
console.error('Unexpected GPT-4 response or missing redirect URL:', gptResponse);
}
} catch (error) {
console.error('Error calling webhook:', error);
} finally {
setIsLoading(false);
}
}
};

return (
<div className="container mx-auto px-6 py-12">
{isLoading ? (
<div>Loading...</div>
) : (
<iframe
ref={iframeRef}
data-tally-src="https://tally.so/embed/3E118B?alignLeft=1&hideTitle=1&transparentBackground=1&dynamicHeight=1"
loading="lazy"
width="100%"
height="1239"
frameBorder="0"
marginHeight={0}
marginWidth={0}
title="AI Engineer Application"
></iframe>
)}
</div>
);
};

export default ApplicationForm;
import React, { useEffect, useRef, useState } from 'react';
import { useHistory } from 'react-router-dom';

declare global {
interface Window {
Tally: any;
}
}

const ApplicationForm: React.FC = () => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [isLoading, setIsLoading] = useState(false);
const history = useHistory();

useEffect(() => {
const script = document.createElement('script');
script.src = 'https://tally.so/widgets/embed.js';
script.async = true;
script.onload = () => {
if (window.Tally) {
window.Tally.loadEmbeds();
} else {
document.querySelectorAll("iframe[data-tally-src]:not([src])").forEach((iframe) => {
iframe.setAttribute('src', iframe.getAttribute('data-tally-src')!);
});
}
};
script.onerror = () => {
document.querySelectorAll("iframe[data-tally-src]:not([src])").forEach((iframe) => {
iframe.setAttribute('src', iframe.getAttribute('data-tally-src')!);
});
};

document.body.appendChild(script);

window.addEventListener('message', handleFormSubmit);

return () => {
document.body.removeChild(script);
window.removeEventListener('message', handleFormSubmit);
};
}, []);

const handleFormSubmit = async (event: MessageEvent) => {
console.log('Received message event:', event);
console.log('Event data:', event.data);
console.log('Event origin:', event.origin);
console.log('Event type:', event.type);

if (event.origin === 'https://tally.so' && event.data?.event === 'Tally.FormSubmitted') {
const formData = event.data.payload;
console.log('Form data received:', formData);

setIsLoading(true);

try {
const response = await fetch('https://b168-99-250-72-13.ngrok-free.app/webhook/callback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});

console.log('Response received:', response);

if (!response.ok) {
throw new Error('Error calling webhook');
}

const data = await response.json();
console.log('Response data:', data);

const gptResponse = data.gptResponse;
console.log('GPT-4 Response:', gptResponse);

const redirectUrl = data.redirectUrl;
console.log('Redirect URL:', redirectUrl);

if (redirectUrl) {
console.log('Redirecting to', redirectUrl);
history.push(redirectUrl);
} else {
console.error('Unexpected GPT-4 response or missing redirect URL:', gptResponse);
}
} catch (error) {
console.error('Error calling webhook:', error);
} finally {
setIsLoading(false);
}
}
};

return (
<div className="container mx-auto px-6 py-12">
{isLoading ? (
<div>Loading...</div>
) : (
<iframe
ref={iframeRef}
data-tally-src="https://tally.so/embed/3E118B?alignLeft=1&hideTitle=1&transparentBackground=1&dynamicHeight=1"
loading="lazy"
width="100%"
height="1239"
frameBorder="0"
marginHeight={0}
marginWidth={0}
title="AI Engineer Application"
></iframe>
)}
</div>
);
};

export default ApplicationForm;
10 Replies
kapa.ai
kapa.ai2mo ago
Hi @Jacob Ferrari. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: Sorry @Jacob Ferrari, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
djakro
djakro2mo ago
@kapa.ai
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: Sorry @Jacob Ferrari, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
martinsos
martinsos2mo ago
Ok let me take a look! I will repost your code but with js afte the three backticks to get it to color it nicely so we can look at it more easily:
import React, { useEffect, useRef, useState } from 'react';
import { useHistory } from 'react-router-dom';

declare global {
interface Window {
Tally: any;
}
}

const ApplicationForm: React.FC = () => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [isLoading, setIsLoading] = useState(false);
const history = useHistory();

useEffect(() => {
const script = document.createElement('script');
script.src = 'https://tally.so/widgets/embed.js';
script.async = true;
script.onload = () => {
if (window.Tally) {
window.Tally.loadEmbeds();
} else {
document.querySelectorAll("iframe[data-tally-src]:not([src])").forEach((iframe) => {
iframe.setAttribute('src', iframe.getAttribute('data-tally-src')!);
});
}
};
script.onerror = () => {
document.querySelectorAll("iframe[data-tally-src]:not([src])").forEach((iframe) => {
iframe.setAttribute('src', iframe.getAttribute('data-tally-src')!);
});
};

document.body.appendChild(script);

window.addEventListener('message', handleFormSubmit);

return () => {
document.body.removeChild(script);
window.removeEventListener('message', handleFormSubmit);
};
}, []);

const handleFormSubmit = async (event: MessageEvent) => {
console.log('Received message event:', event);
console.log('Event data:', event.data);
console.log('Event origin:', event.origin);
console.log('Event type:', event.type);

if (event.origin === 'https://tally.so' && event.data?.event === 'Tally.FormSubmitted') {
const formData = event.data.payload;
console.log('Form data received:', formData);

setIsLoading(true);

try {
const response = await fetch('https://b168-99-250-72-13.ngrok-free.app/webhook/callback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});

console.log('Response received:', response);

if (!response.ok) {
throw new Error('Error calling webhook');
}

const data = await response.json();
console.log('Response data:', data);

const gptResponse = data.gptResponse;
console.log('GPT-4 Response:', gptResponse);

const redirectUrl = data.redirectUrl;
console.log('Redirect URL:', redirectUrl);

if (redirectUrl) {
console.log('Redirecting to', redirectUrl);
history.push(redirectUrl);
} else {
console.error('Unexpected GPT-4 response or missing redirect URL:', gptResponse);
}
} catch (error) {
console.error('Error calling webhook:', error);
} finally {
setIsLoading(false);
}
}
};

return (
<div className="container mx-auto px-6 py-12">
{isLoading ? (
<div>Loading...</div>
) : (
<iframe
ref={iframeRef}
data-tally-src="https://tally.so/embed/3E118B?alignLeft=1&hideTitle=1&transparentBackground=1&dynamicHeight=1"
loading="lazy"
width="100%"
height="1239"
frameBorder="0"
marginHeight={0}
marginWidth={0}
title="AI Engineer Application"
></iframe>
)}
</div>
);
};

export default ApplicationForm;
import React, { useEffect, useRef, useState } from 'react';
import { useHistory } from 'react-router-dom';

declare global {
interface Window {
Tally: any;
}
}

const ApplicationForm: React.FC = () => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [isLoading, setIsLoading] = useState(false);
const history = useHistory();

useEffect(() => {
const script = document.createElement('script');
script.src = 'https://tally.so/widgets/embed.js';
script.async = true;
script.onload = () => {
if (window.Tally) {
window.Tally.loadEmbeds();
} else {
document.querySelectorAll("iframe[data-tally-src]:not([src])").forEach((iframe) => {
iframe.setAttribute('src', iframe.getAttribute('data-tally-src')!);
});
}
};
script.onerror = () => {
document.querySelectorAll("iframe[data-tally-src]:not([src])").forEach((iframe) => {
iframe.setAttribute('src', iframe.getAttribute('data-tally-src')!);
});
};

document.body.appendChild(script);

window.addEventListener('message', handleFormSubmit);

return () => {
document.body.removeChild(script);
window.removeEventListener('message', handleFormSubmit);
};
}, []);

const handleFormSubmit = async (event: MessageEvent) => {
console.log('Received message event:', event);
console.log('Event data:', event.data);
console.log('Event origin:', event.origin);
console.log('Event type:', event.type);

if (event.origin === 'https://tally.so' && event.data?.event === 'Tally.FormSubmitted') {
const formData = event.data.payload;
console.log('Form data received:', formData);

setIsLoading(true);

try {
const response = await fetch('https://b168-99-250-72-13.ngrok-free.app/webhook/callback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});

console.log('Response received:', response);

if (!response.ok) {
throw new Error('Error calling webhook');
}

const data = await response.json();
console.log('Response data:', data);

const gptResponse = data.gptResponse;
console.log('GPT-4 Response:', gptResponse);

const redirectUrl = data.redirectUrl;
console.log('Redirect URL:', redirectUrl);

if (redirectUrl) {
console.log('Redirecting to', redirectUrl);
history.push(redirectUrl);
} else {
console.error('Unexpected GPT-4 response or missing redirect URL:', gptResponse);
}
} catch (error) {
console.error('Error calling webhook:', error);
} finally {
setIsLoading(false);
}
}
};

return (
<div className="container mx-auto px-6 py-12">
{isLoading ? (
<div>Loading...</div>
) : (
<iframe
ref={iframeRef}
data-tally-src="https://tally.so/embed/3E118B?alignLeft=1&hideTitle=1&transparentBackground=1&dynamicHeight=1"
loading="lazy"
width="100%"
height="1239"
frameBorder="0"
marginHeight={0}
marginWidth={0}
title="AI Engineer Application"
></iframe>
)}
</div>
);
};

export default ApplicationForm;
djakro
djakro2mo ago
Here is the webhook part.
js
import express from 'express';
import { type WebhookCallback } from 'wasp/server/api';
import { type MiddlewareConfigFn } from 'wasp/server';
import { processWithGPT4 } from 'wasp/server/operations';

export const webhookCallback: WebhookCallback = async (req, res, context) => {
try {
const formDataBuffer = req.body;
console.log('Received webhook data:', formDataBuffer);

// Convert Buffer to JSON
const formData = JSON.parse(formDataBuffer.toString());
console.log('Parsed webhook data:', formData);

// Create the prompt
const prompt = `Output "1" if the user has answered the questions decently and above, they just have to have minimum decent answers.

Output "2" if the user did not do well with the questions and really wouldn't be a good fit: ${JSON.stringify(formData)} ONLY output 1 or 2 NOTHING ELSE.`;

// Extract temperature from formData, if available
const temperature = formData.temperature !== undefined ? formData.temperature : 0.3;

// Call the processWithGPT4 action
const gptResponse = await processWithGPT4({ data: formData, prompt, temperature }, { ...context, isWebhook: true });

console.log('GPT-4 Response:', gptResponse);

// Determine the redirect URL based on the GPT-4 response
const redirectUrl = gptResponse.trim() === '1' ? '/interview' : '/not-a-good-fit';

const responsePayload = { message: 'Webhook received and processed', gptResponse, redirectUrl };
console.log('Response payload:', responsePayload);

res.status(200).json(responsePayload);
} catch (error) {
console.error('Error processing webhook:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
};

export const webhookCallbackMiddlewareFn: MiddlewareConfigFn = (middlewareConfig) => {
console.log('webhookCallbackMiddlewareFn: Swap express.json for express.raw');

middlewareConfig.delete('express.json');
middlewareConfig.set('express.raw', express.raw({ type: '*/*' }));

return middlewareConfig;
};
js
import express from 'express';
import { type WebhookCallback } from 'wasp/server/api';
import { type MiddlewareConfigFn } from 'wasp/server';
import { processWithGPT4 } from 'wasp/server/operations';

export const webhookCallback: WebhookCallback = async (req, res, context) => {
try {
const formDataBuffer = req.body;
console.log('Received webhook data:', formDataBuffer);

// Convert Buffer to JSON
const formData = JSON.parse(formDataBuffer.toString());
console.log('Parsed webhook data:', formData);

// Create the prompt
const prompt = `Output "1" if the user has answered the questions decently and above, they just have to have minimum decent answers.

Output "2" if the user did not do well with the questions and really wouldn't be a good fit: ${JSON.stringify(formData)} ONLY output 1 or 2 NOTHING ELSE.`;

// Extract temperature from formData, if available
const temperature = formData.temperature !== undefined ? formData.temperature : 0.3;

// Call the processWithGPT4 action
const gptResponse = await processWithGPT4({ data: formData, prompt, temperature }, { ...context, isWebhook: true });

console.log('GPT-4 Response:', gptResponse);

// Determine the redirect URL based on the GPT-4 response
const redirectUrl = gptResponse.trim() === '1' ? '/interview' : '/not-a-good-fit';

const responsePayload = { message: 'Webhook received and processed', gptResponse, redirectUrl };
console.log('Response payload:', responsePayload);

res.status(200).json(responsePayload);
} catch (error) {
console.error('Error processing webhook:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
};

export const webhookCallbackMiddlewareFn: MiddlewareConfigFn = (middlewareConfig) => {
console.log('webhookCallbackMiddlewareFn: Swap express.json for express.raw');

middlewareConfig.delete('express.json');
middlewareConfig.set('express.raw', express.raw({ type: '*/*' }));

return middlewareConfig;
};
martinsos
martinsos2mo ago
Ok, so this line console.log('Redirecting to', redirectUrl);, just above history.push, does that trigger? What does it print in your browser dev console? Btw history should be working in general, here is a link to discord convo where Wasp user used it successfully just recently: https://discord.com/channels/686873244791210014/1249056983802183740/1249743840500387931 . So "js" -> put it as a very next character after the last of the three backticks. So you want to have:
martinsos
martinsos2mo ago
No description
JLegendz
JLegendz2mo ago
@Jacob Ferrari , hi, did you ever figure this out? I was going to suggest something along the lines of
await response.json().then(res => {
try {
history.push(res.redirectUrl);
} catch (err) {
console.log(err)
}
})
}
await response.json().then(res => {
try {
history.push(res.redirectUrl);
} catch (err) {
console.log(err)
}
})
}
I do something similar in my project. I think you just want to make sure the redirectUrl is ready, and THEN redirect. Just make sure you aren't redirecting prior to having the data. Hope you firgure it out!
djakro
djakro2mo ago
Hi @JLegendz Thanks for this!! That is probably the solution but putting it on the backburner for now while i tackle other projects!
JLegendz
JLegendz2mo ago
No worries!