feat: align integrations
This commit is contained in:
@@ -72,16 +72,16 @@ export const RobotIntegrationPage = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const pathSegments = location.pathname.split('/');
|
const pathSegments = location.pathname.split('/');
|
||||||
const robotsIndex = pathSegments.findIndex(segment => segment === 'robots' || segment === 'prebuilt-robots');
|
const robotsIndex = pathSegments.findIndex(segment => segment === 'robots' || segment === 'prebuilt-robots');
|
||||||
const integrateIndex = pathSegments.findIndex(segment => segment === 'integrate');
|
const integrateIndex = pathSegments.findIndex(segment => segment === 'integrate');
|
||||||
|
|
||||||
const robotIdFromUrl = robotsIndex !== -1 && robotsIndex + 1 < pathSegments.length
|
const robotIdFromUrl = robotsIndex !== -1 && robotsIndex + 1 < pathSegments.length
|
||||||
? pathSegments[robotsIndex + 1]
|
? pathSegments[robotsIndex + 1]
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const integrationType = integrateIndex !== -1 && integrateIndex + 1 < pathSegments.length
|
const integrationType = integrateIndex !== -1 && integrateIndex + 1 < pathSegments.length
|
||||||
? pathSegments[integrateIndex + 1] as "googleSheets" | "airtable" | "webhook"
|
? pathSegments[integrateIndex + 1] as "googleSheets" | "airtable" | "webhook"
|
||||||
: preSelectedIntegrationType || null;
|
: preSelectedIntegrationType || null;
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ export const RobotIntegrationPage = ({
|
|||||||
const [urlError, setUrlError] = useState<string | null>(null);
|
const [urlError, setUrlError] = useState<string | null>(null);
|
||||||
|
|
||||||
const { recordingId: recordingIdFromStore, notify, setRerenderRobots, setRecordingId } = useGlobalInfoStore();
|
const { recordingId: recordingIdFromStore, notify, setRerenderRobots, setRecordingId } = useGlobalInfoStore();
|
||||||
|
|
||||||
const recordingId = robotIdFromUrl || recordingIdFromStore;
|
const recordingId = robotIdFromUrl || recordingIdFromStore;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -137,7 +137,7 @@ export const RobotIntegrationPage = ({
|
|||||||
const redirectUrl = `${window.location.origin}${basePath}/${recordingId}/integrate/googleSheets`;
|
const redirectUrl = `${window.location.origin}${basePath}/${recordingId}/integrate/googleSheets`;
|
||||||
window.location.href = `${apiUrl}/auth/google?robotId=${recordingId}&redirectUrl=${encodeURIComponent(redirectUrl)}`;
|
window.location.href = `${apiUrl}/auth/google?robotId=${recordingId}&redirectUrl=${encodeURIComponent(redirectUrl)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const authenticateWithAirtable = () => {
|
const authenticateWithAirtable = () => {
|
||||||
if (!recordingId) {
|
if (!recordingId) {
|
||||||
console.error("Cannot authenticate: recordingId is null");
|
console.error("Cannot authenticate: recordingId is null");
|
||||||
@@ -251,7 +251,7 @@ export const RobotIntegrationPage = ({
|
|||||||
const deleteWebhookSetting = async (webhookId: string) => {
|
const deleteWebhookSetting = async (webhookId: string) => {
|
||||||
if (!recordingId) return;
|
if (!recordingId) return;
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const response = await removeWebhook(webhookId, recordingId);
|
const response = await removeWebhook(webhookId, recordingId);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setSettings((prev) => ({ ...prev, webhooks: (prev.webhooks || []).filter((webhook) => webhook.id !== webhookId) }));
|
setSettings((prev) => ({ ...prev, webhooks: (prev.webhooks || []).filter((webhook) => webhook.id !== webhookId) }));
|
||||||
@@ -681,36 +681,60 @@ export const RobotIntegrationPage = ({
|
|||||||
if (!selectedIntegrationType && !integrationType) {
|
if (!selectedIntegrationType && !integrationType) {
|
||||||
return (
|
return (
|
||||||
<RobotConfigPage title="Integration" onCancel={handleCancel} cancelButtonText={t("robot_edit.cancel")} showSaveButton={false} backToSelectionText={"← " + t("right_panel.buttons.back")} onBackToSelection={() => navigate(`/${robotPath}/${recordingId}/integrate`)}>
|
<RobotConfigPage title="Integration" onCancel={handleCancel} cancelButtonText={t("robot_edit.cancel")} showSaveButton={false} backToSelectionText={"← " + t("right_panel.buttons.back")} onBackToSelection={() => navigate(`/${robotPath}/${recordingId}/integrate`)}>
|
||||||
<div style={{ display: "flex", flexDirection: "column", alignItems: "flex-start", position: "relative", minHeight: "400px" }}>
|
<div
|
||||||
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", padding: "20px", width: "100%" }}>
|
style={{
|
||||||
<div style={{ display: "flex", gap: "20px" }}>
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
position: "relative",
|
||||||
|
minHeight: "400px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "repeat(4, minmax(160px, 1fr))",
|
||||||
|
gap: "20px",
|
||||||
|
justifyContent: "start",
|
||||||
|
maxWidth: "900px",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Button variant="outlined" onClick={() => {
|
<Button variant="outlined" onClick={() => {
|
||||||
if (!recordingId) return;
|
if (!recordingId) return;
|
||||||
setSelectedIntegrationType("googleSheets");
|
setSelectedIntegrationType("googleSheets");
|
||||||
setSettings({ ...settings, integrationType: "googleSheets" });
|
setSettings({ ...settings, integrationType: "googleSheets" });
|
||||||
const basePath = robotPath === "prebuilt-robots" ? "/prebuilt-robots" : "/robots";
|
const basePath = robotPath === "prebuilt-robots" ? "/prebuilt-robots" : "/robots";
|
||||||
navigate(`${basePath}/${recordingId}/integrate/googleSheets`);
|
navigate(`${basePath}/${recordingId}/integrate/googleSheets`);
|
||||||
}} style={{ display: "flex", flexDirection: "column", alignItems: "center", background: 'white', color: '#ff00c3' }}>
|
}} style={{ display: "flex", flexDirection: "column", alignItems: "center", background: 'white', color: '#ff00c3' }}>
|
||||||
<img src="https://ik.imagekit.io/ys1blv5kv/gsheet.svg" alt="Google Sheets" style={{ margin: "6px" }} />
|
<img src="https://ik.imagekit.io/ys1blv5kv/gsheet.svg" alt="Google Sheets" style={{ margin: "6px" }} />
|
||||||
Google Sheets
|
Google Sheets
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outlined" onClick={() => {
|
<Button variant="outlined" onClick={() => {
|
||||||
if (!recordingId) return;
|
if (!recordingId) return;
|
||||||
setSelectedIntegrationType("airtable");
|
setSelectedIntegrationType("airtable");
|
||||||
setSettings({ ...settings, integrationType: "airtable" });
|
setSettings({ ...settings, integrationType: "airtable" });
|
||||||
const basePath = robotPath === "prebuilt-robots" ? "/prebuilt-robots" : "/robots";
|
const basePath = robotPath === "prebuilt-robots" ? "/prebuilt-robots" : "/robots";
|
||||||
navigate(`${basePath}/${recordingId}/integrate/airtable`);
|
navigate(`${basePath}/${recordingId}/integrate/airtable`);
|
||||||
}} style={{ display: "flex", flexDirection: "column", alignItems: "center", background: 'white', color: '#ff00c3' }}>
|
}} style={{ display: "flex", flexDirection: "column", alignItems: "center", background: 'white', color: '#ff00c3' }}>
|
||||||
<img src="https://ik.imagekit.io/ys1blv5kv/airtable.svg" alt="Airtable" style={{ margin: "6px" }} />
|
<img src="https://ik.imagekit.io/ys1blv5kv/airtable.svg" alt="Airtable" style={{ margin: "6px" }} />
|
||||||
Airtable
|
Airtable
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outlined" onClick={() => {
|
<Button variant="outlined" onClick={() => {
|
||||||
if (!recordingId) return;
|
if (!recordingId) return;
|
||||||
setSelectedIntegrationType("webhook");
|
setSelectedIntegrationType("webhook");
|
||||||
setSettings({ ...settings, integrationType: "webhook" });
|
setSettings({ ...settings, integrationType: "webhook" });
|
||||||
const basePath = robotPath === "prebuilt-robots" ? "/prebuilt-robots" : "/robots";
|
const basePath = robotPath === "prebuilt-robots" ? "/prebuilt-robots" : "/robots";
|
||||||
navigate(`${basePath}/${recordingId}/integrate/webhook`);
|
navigate(`${basePath}/${recordingId}/integrate/webhook`);
|
||||||
}} style={{ display: "flex", flexDirection: "column", alignItems: "center", background: 'white', color: '#ff00c3' }}>
|
}} style={{ display: "flex", flexDirection: "column", alignItems: "center", background: 'white', color: '#ff00c3' }}>
|
||||||
<img src="/svg/webhook.svg" alt="Webhook" style={{ margin: "6px" }} />
|
<img src="/svg/webhook.svg" alt="Webhook" style={{ margin: "6px" }} />
|
||||||
Webhooks
|
Webhooks
|
||||||
</Button>
|
</Button>
|
||||||
@@ -763,9 +787,9 @@ export const RobotIntegrationPage = ({
|
|||||||
{settings.webhooks.map((webhook) => (
|
{settings.webhooks.map((webhook) => (
|
||||||
<TableRow key={webhook.id}>
|
<TableRow key={webhook.id}>
|
||||||
<TableCell>{webhook.url}</TableCell>
|
<TableCell>{webhook.url}</TableCell>
|
||||||
<TableCell><Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>{webhook.events.map((event) => (<Chip key={event} label={formatEventName(event)} size="small" variant="outlined"/>))}</Box></TableCell>
|
<TableCell><Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>{webhook.events.map((event) => (<Chip key={event} label={formatEventName(event)} size="small" variant="outlined" />))}</Box></TableCell>
|
||||||
<TableCell>{formatLastCalled(webhook.lastCalledAt)}</TableCell>
|
<TableCell>{formatLastCalled(webhook.lastCalledAt)}</TableCell>
|
||||||
<TableCell><Switch checked={webhook.active} onChange={() => toggleWebhookStatusSetting(webhook.id)} size="small"/></TableCell>
|
<TableCell><Switch checked={webhook.active} onChange={() => toggleWebhookStatusSetting(webhook.id)} size="small" /></TableCell>
|
||||||
<TableCell><Box sx={{ display: "flex", gap: "8px" }}>
|
<TableCell><Box sx={{ display: "flex", gap: "8px" }}>
|
||||||
<IconButton size="small" onClick={() => testWebhookSetting(webhook.id)} disabled={loading || !webhook.active} title="Test"><ScienceIcon fontSize="small" /></IconButton>
|
<IconButton size="small" onClick={() => testWebhookSetting(webhook.id)} disabled={loading || !webhook.active} title="Test"><ScienceIcon fontSize="small" /></IconButton>
|
||||||
<IconButton size="small" onClick={() => editWebhookSetting(webhook)} disabled={loading} title="Edit"><EditIcon fontSize="small" /></IconButton>
|
<IconButton size="small" onClick={() => editWebhookSetting(webhook)} disabled={loading} title="Edit"><EditIcon fontSize="small" /></IconButton>
|
||||||
@@ -780,7 +804,7 @@ export const RobotIntegrationPage = ({
|
|||||||
{!showWebhookForm && (
|
{!showWebhookForm && (
|
||||||
<Box sx={{ marginBottom: "20px", width: "100%" }}>
|
<Box sx={{ marginBottom: "20px", width: "100%" }}>
|
||||||
<Box sx={{ display: "flex", gap: "15px", alignItems: "center", marginBottom: "15px" }}>
|
<Box sx={{ display: "flex", gap: "15px", alignItems: "center", marginBottom: "15px" }}>
|
||||||
<TextField label="Webhook URL" placeholder="https://your-api.com/webhook/endpoint" sx={{ flex: 1 }} value={newWebhook.url} onChange={(e) => { setNewWebhook({ ...newWebhook, url: e.target.value }); if (urlError) setUrlError(null); }} error={!!urlError} helperText={urlError} required aria-describedby="webhook-url-help"/>
|
<TextField label="Webhook URL" placeholder="https://your-api.com/webhook/endpoint" sx={{ flex: 1 }} value={newWebhook.url} onChange={(e) => { setNewWebhook({ ...newWebhook, url: e.target.value }); if (urlError) setUrlError(null); }} error={!!urlError} helperText={urlError} required aria-describedby="webhook-url-help" />
|
||||||
<TextField select label="When" value={newWebhook.events[0] || "run_completed"} onChange={(e) => setNewWebhook({ ...newWebhook, events: [e.target.value] })} sx={{ minWidth: "200px" }} required>
|
<TextField select label="When" value={newWebhook.events[0] || "run_completed"} onChange={(e) => setNewWebhook({ ...newWebhook, events: [e.target.value] })} sx={{ minWidth: "200px" }} required>
|
||||||
<MenuItem value="run_completed">Run finished</MenuItem>
|
<MenuItem value="run_completed">Run finished</MenuItem>
|
||||||
<MenuItem value="run_failed">Run failed</MenuItem>
|
<MenuItem value="run_failed">Run failed</MenuItem>
|
||||||
@@ -796,13 +820,13 @@ export const RobotIntegrationPage = ({
|
|||||||
<Card sx={{ width: "100%", marginBottom: "20px" }}>
|
<Card sx={{ width: "100%", marginBottom: "20px" }}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="h6" sx={{ marginBottom: "20px" }}>{editingWebhook ? "Edit Webhook" : "Add New Webhook"}</Typography>
|
<Typography variant="h6" sx={{ marginBottom: "20px" }}>{editingWebhook ? "Edit Webhook" : "Add New Webhook"}</Typography>
|
||||||
<TextField fullWidth label="Webhook URL" value={newWebhook.url} onChange={(e) => { setNewWebhook({ ...newWebhook, url: e.target.value }); if (urlError) setUrlError(null); }} sx={{ marginBottom: "15px" }} placeholder="https://your-api.com/webhook/endpoint" required error={!!urlError} helperText={urlError}/>
|
<TextField fullWidth label="Webhook URL" value={newWebhook.url} onChange={(e) => { setNewWebhook({ ...newWebhook, url: e.target.value }); if (urlError) setUrlError(null); }} sx={{ marginBottom: "15px" }} placeholder="https://your-api.com/webhook/endpoint" required error={!!urlError} helperText={urlError} />
|
||||||
<TextField fullWidth select label="Call when" value={newWebhook.events} onChange={(e) => setNewWebhook({ ...newWebhook, events: typeof e.target.value === "string" ? [e.target.value] : e.target.value })}
|
<TextField fullWidth select label="Call when" value={newWebhook.events} onChange={(e) => setNewWebhook({ ...newWebhook, events: typeof e.target.value === "string" ? [e.target.value] : e.target.value })}
|
||||||
SelectProps={{ multiple: true, renderValue: (selected) => (<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>{(selected as string[]).map((value) => (<Chip key={value} label={formatEventName(value)} size="small"/>))}</Box>),}} sx={{ marginBottom: "20px" }} required>
|
SelectProps={{ multiple: true, renderValue: (selected) => (<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>{(selected as string[]).map((value) => (<Chip key={value} label={formatEventName(value)} size="small" />))}</Box>), }} sx={{ marginBottom: "20px" }} required>
|
||||||
<MenuItem value="run_completed">Run finished</MenuItem>
|
<MenuItem value="run_completed">Run finished</MenuItem>
|
||||||
<MenuItem value="run_failed">Run failed</MenuItem>
|
<MenuItem value="run_failed">Run failed</MenuItem>
|
||||||
</TextField>
|
</TextField>
|
||||||
<FormControlLabel control={<Switch checked={newWebhook.active} onChange={(e) => setNewWebhook({ ...newWebhook, active: e.target.checked })}/>} label="Active" sx={{ marginBottom: "10px" }}/>
|
<FormControlLabel control={<Switch checked={newWebhook.active} onChange={(e) => setNewWebhook({ ...newWebhook, active: e.target.checked })} />} label="Active" sx={{ marginBottom: "10px" }} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardActions>
|
<CardActions>
|
||||||
<Button variant="contained" color="primary" onClick={editingWebhook ? updateWebhookSetting : addWebhookSetting} disabled={!newWebhook.url || !newWebhook.events || newWebhook.events.length === 0 || loading || !!urlError}>
|
<Button variant="contained" color="primary" onClick={editingWebhook ? updateWebhookSetting : addWebhookSetting} disabled={!newWebhook.url || !newWebhook.events || newWebhook.events.length === 0 || loading || !!urlError}>
|
||||||
|
|||||||
Reference in New Issue
Block a user