diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index c2994942..268134f4 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -672,8 +672,17 @@ router.get("/airtable/callback", async (req, res) => { if (err) console.error('Session cleanup error:', err); }); + res.cookie("airtable_auth_status", "success", { + httpOnly: false, + maxAge: 60000, + }); // 1-minute expiration + res.cookie("airtable_auth_message", "Robot successfully authenticated", { + httpOnly: false, + maxAge: 60000, + }); + res.redirect( - `${process.env.PUBLIC_URL}/robots/${state}/integrate?success=true` + `${process.env.PUBLIC_URL}/robots/${state}/integrate || http://localhost:5173/robots/${state}/integrate` ); } catch (error: any) { diff --git a/src/components/integration/IntegrationSettings.tsx b/src/components/integration/IntegrationSettings.tsx index 1d0534d1..9dd3bdc9 100644 --- a/src/components/integration/IntegrationSettings.tsx +++ b/src/components/integration/IntegrationSettings.tsx @@ -6,10 +6,9 @@ import { CircularProgress, Alert, AlertTitle, - Chip, + Button, + TextField, } from "@mui/material"; -import Button from "@mui/material/Button"; -import TextField from "@mui/material/TextField"; import axios from "axios"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { getStoredRecording } from "../../api/storage"; @@ -52,9 +51,6 @@ export const IntegrationSettingsModal = ({ handleClose, }: IntegrationProps) => { const { t } = useTranslation(); - const [selectedIntegrationType, setSelectedIntegrationType] = useState< - "googleSheets" | "airtable" | null - >(null); const [settings, setSettings] = useState({ spreadsheetId: "", spreadsheetName: "", @@ -64,114 +60,61 @@ export const IntegrationSettingsModal = ({ integrationType: "googleSheets", }); - const [airtableTables, setAirtableTables] = useState<{ id: string; name: string }[]>([]); - const [spreadsheets, setSpreadsheets] = useState< - { id: string; name: string }[] - >([]); - const [airtableBases, setAirtableBases] = useState< - { id: string; name: string }[] - >([]); + const [spreadsheets, setSpreadsheets] = useState<{ id: string; name: string }[]>([]); + const [airtableBases, setAirtableBases] = useState<{ id: string; name: string }[]>([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const { recordingId, notify } = useGlobalInfoStore(); const [recording, setRecording] = useState(null); + const [airtableAuthStatus, setAirtableAuthStatus] = useState(null); + + // Authenticate with Google Sheets const authenticateWithGoogle = () => { window.location.href = `${apiUrl}/auth/google?robotId=${recordingId}`; }; + // Authenticate with Airtable const authenticateWithAirtable = () => { window.location.href = `${apiUrl}/auth/airtable?robotId=${recordingId}`; }; - const handleIntegrationType = (type: "googleSheets" | "airtable") => { - setSelectedIntegrationType(type); - setSettings({ - ...settings, - integrationType: type, - }); - }; - - const fetchAirtableTables = async (baseId: string) => { - try { - const response = await axios.get( - `${apiUrl}/auth/airtable/tables?baseId=${baseId}&robotId=${recordingId}`, - { - withCredentials: true, - } - ); - setAirtableTables(response.data); - } catch (error: any) { - console.error( - "Error fetching Airtable tables:", - error.response?.data?.message || error.message - ); - notify( - "error", - t("integration_settings.errors.fetch_error", { - message: error.response?.data?.message || error.message, - }) - ); - } - }; - + // Fetch Google Sheets files const fetchSpreadsheetFiles = async () => { try { const response = await axios.get( `${apiUrl}/auth/gsheets/files?robotId=${recordingId}`, - { - withCredentials: true, - } + { withCredentials: true } ); setSpreadsheets(response.data); } catch (error: any) { - console.error( - "Error fetching spreadsheet files:", - error.response?.data?.message || error.message - ); - notify( - "error", - t("integration_settings.errors.fetch_error", { - message: error.response?.data?.message || error.message, - }) - ); + console.error("Error fetching spreadsheet files:", error); + notify("error", t("integration_settings.errors.fetch_error", { + message: error.response?.data?.message || error.message, + })); } }; - console.log("recordingId", recordingId); - + // Fetch Airtable bases const fetchAirtableBases = async () => { try { const response = await axios.get( `${apiUrl}/auth/airtable/bases?robotId=${recordingId}`, - { - withCredentials: true, - } + { withCredentials: true } ); - setAirtableBases(response.data); - - console.log("Airtable bases:", response.data); - } catch (error: any) { - console.error( - "Error fetching Airtable bases:", - error.response?.data?.message || error.message - ); - notify( - "error", - t("integration_settings.errors.fetch_error", { - message: error.response?.data?.message || error.message, - }) - ); + console.error("Error fetching Airtable bases:", error); + notify("error", t("integration_settings.errors.fetch_error", { + message: error.response?.data?.message || error.message, + })); } }; + // Handle Google Sheets selection const handleSpreadsheetSelect = (e: React.ChangeEvent) => { - const selectedSheet = spreadsheets.find( - (sheet) => sheet.id === e.target.value - ); + const selectedSheet = spreadsheets.find((sheet) => sheet.id === e.target.value); if (selectedSheet) { setSettings({ ...settings, @@ -181,10 +124,9 @@ export const IntegrationSettingsModal = ({ } }; + // Handle Airtable base selection const handleAirtableBaseSelect = (e: React.ChangeEvent) => { - const selectedBase = airtableBases.find( - (base) => base.id === e.target.value - ); + const selectedBase = airtableBases.find((base) => base.id === e.target.value); if (selectedBase) { setSettings({ ...settings, @@ -194,9 +136,10 @@ export const IntegrationSettingsModal = ({ } }; + // Update Google Sheets integration const updateGoogleSheetId = async () => { try { - const response = await axios.post( + await axios.post( `${apiUrl}/auth/gsheets/update`, { spreadsheetId: settings.spreadsheetId, @@ -205,19 +148,19 @@ export const IntegrationSettingsModal = ({ }, { withCredentials: true } ); - notify(`success`, t("integration_settings.notifications.sheet_selected")); - console.log("Google Sheet ID updated:", response.data); + notify("success", t("integration_settings.notifications.sheet_selected")); } catch (error: any) { - console.error( - "Error updating Google Sheet ID:", - error.response?.data?.message || error.message - ); + console.error("Error updating Google Sheet ID:", error); + notify("error", t("integration_settings.errors.update_error", { + message: error.response?.data?.message || error.message, + })); } }; - const updateAirtableBaseId = async () => { + // Update Airtable integration + const updateAirtableBase = async () => { try { - const response = await axios.post( + await axios.post( `${apiUrl}/auth/airtable/update`, { baseId: settings.airtableBaseId, @@ -226,332 +169,361 @@ export const IntegrationSettingsModal = ({ }, { withCredentials: true } ); - notify(`success`, t("integration_settings.notifications.base_selected")); - console.log("Airtable Base ID updated:", response.data); + notify("success", t("integration_settings.notifications.base_selected")); } catch (error: any) { - console.error( - "Error updating Airtable Base ID:", - error.response?.data?.message || error.message - ); + console.error("Error updating Airtable base:", error); + notify("error", t("integration_settings.errors.update_error", { + message: error.response?.data?.message || error.message, + })); } }; - const removeIntegration = async () => { + // Remove Google Sheets integration + const removeGoogleSheetsIntegration = async () => { try { - const endpoint = - selectedIntegrationType === "googleSheets" - ? "/auth/gsheets/remove" - : "/auth/airtable/remove"; - await axios.post( - `${apiUrl}${endpoint}`, + `${apiUrl}/auth/gsheets/remove`, { robotId: recordingId }, { withCredentials: true } ); - - setRecording(null); setSpreadsheets([]); - setAirtableBases([]); - setSelectedIntegrationType(null); - setSettings({ - spreadsheetId: "", - spreadsheetName: "", - airtableBaseId: "", - airtableBaseName: "", - data: "", - integrationType: "googleSheets", - }); + setSettings({ ...settings, spreadsheetId: "", spreadsheetName: "" }); + notify("success", t("integration_settings.notifications.integration_removed")); } catch (error: any) { - console.error( - "Error removing integration:", - error.response?.data?.message || error.message - ); + console.error("Error removing Google Sheets integration:", error); + notify("error", t("integration_settings.errors.remove_error", { + message: error.response?.data?.message || error.message, + })); } }; + // Remove Airtable integration + const removeAirtableIntegration = async () => { + try { + await axios.post( + `${apiUrl}/auth/airtable/remove`, + { robotId: recordingId }, + { withCredentials: true } + ); + setAirtableBases([]); + setSettings({ ...settings, airtableBaseId: "", airtableBaseName: "" }); + notify("success", t("integration_settings.notifications.integration_removed")); + } catch (error: any) { + console.error("Error removing Airtable integration:", error); + notify("error", t("integration_settings.errors.remove_error", { + message: error.response?.data?.message || error.message, + })); + } + }; + + // Handle OAuth callback for Airtable + const handleAirtableOAuthCallback = async () => { + try { + const response = await axios.get(`${apiUrl}/auth/airtable/callback`); + if (response.data.success) { + setAirtableAuthStatus(true); + fetchAirtableBases(); // Fetch bases after successful authentication + } + } catch (error) { + setError("Error authenticating with Airtable"); + } + }; + + // Fetch recording info on component mount useEffect(() => { - const status = getCookie("robot_auth_status"); - const message = getCookie("robot_auth_message"); - - if (status === "success" && message) { - notify("success", message); - removeCookie("robot_auth_status"); - removeCookie("robot_auth_message"); - } - - const urlParams = new URLSearchParams(window.location.search); - const code = urlParams.get("code"); - if (code) { - // Determine which authentication callback to handle - // You'll need to implement similar callback logic for Airtable - } - const fetchRecordingInfo = async () => { if (!recordingId) return; const recording = await getStoredRecording(recordingId); if (recording) { setRecording(recording); - // Determine integration type based on existing integration if (recording.google_sheet_id) { - setSelectedIntegrationType("googleSheets"); + setSettings({ ...settings, integrationType: "googleSheets" }); } else if (recording.airtable_base_id) { - setSelectedIntegrationType("airtable"); + setSettings({ ...settings, integrationType: "airtable" }); } } }; - fetchRecordingInfo(); }, [recordingId]); - // Initial integration type selection - if (!selectedIntegrationType) { - return ( - -
- - {t("integration_settings.title_select_integration")} - -
- - -
+ // Handle Airtable authentication status + useEffect(() => { + const status = getCookie("airtable_auth_status"); + const message = getCookie("airtable_auth_message"); + + if (status === "success" && message) { + notify("success", message); + removeCookie("airtable_auth_status"); + removeCookie("airtable_auth_message"); + setAirtableAuthStatus(true); + fetchAirtableBases(); // Fetch bases after successful authentication + } + + const urlParams = new URLSearchParams(window.location.search); + const code = urlParams.get("code"); + if (code) { + handleAirtableOAuthCallback(); + } + }, [recordingId]); + + console.log(recording) + + + const [selectedIntegrationType, setSelectedIntegrationType] = useState< + "googleSheets" | "airtable" | null +>(null); + +// Add this UI at the top of the modal return statement +if (!selectedIntegrationType) { + return ( + +
+ + {t("integration_settings.title_select_integration")} + +
+ {/* Google Sheets Button */} + + + {/* Airtable Button */} +
- - ); - } +
+
+ ); +} + + return ( -
+
- {selectedIntegrationType === "googleSheets" - ? t("integration_settings.title_google") - : t("integration_settings.title_airtable")} + {t("integration_settings.title")} - {recording && - (recording.google_sheet_id || recording.airtable_base_id ? ( - <> - - - {t("integration_settings.alerts.success.title")} - - {selectedIntegrationType === "googleSheets" ? ( - <> - {t("integration_settings.alerts.success.content", { - sheetName: recording.google_sheet_name, - })} - - {t("integration_settings.alerts.success.here")} - - - ) : ( - <> - {t("integration_settings.alerts.success.content", { - sheetName: recording.airtable_base_name, - })} - - {t("integration_settings.alerts.success.here")} - - - )} -
- - {t("integration_settings.alerts.success.note")} - {" "} - {t("integration_settings.alerts.success.sync_limitation")} -
- - - ) : null)} - - {!recording?.[ - selectedIntegrationType === "googleSheets" - ? "google_sheet_email" - : "airtable_email" - ] ? ( + {/* Google Sheets Integration */} + {settings.integrationType === "googleSheets" && ( <> -

{t("integration_settings.descriptions.sync_info")}

- - - ) : ( - <> - {recording[ - selectedIntegrationType === "googleSheets" - ? "google_sheet_email" - : "airtable_email" - ] && ( - - {t("integration_settings.descriptions.authenticated_as", { - email: - recording[ - selectedIntegrationType === "googleSheets" - ? "google_sheet_email" - : "airtable_email" - ], - })} - - )} - - {loading ? ( - - ) : error ? ( - {error} - ) : (selectedIntegrationType === "googleSheets" - ? spreadsheets - : airtableBases - ).length === 0 ? ( + {recording?.google_sheet_id ? ( <> -
- - -
+ {t("integration_settings.alerts.success.here")} + + + ) : ( <> - - {(selectedIntegrationType === "googleSheets" - ? spreadsheets - : airtableBases - ).map((item) => ( - - {item.name} - - ))} - - - {(selectedIntegrationType === "googleSheets" - ? settings.spreadsheetId - : settings.airtableBaseId) && ( - - {t("integration_settings.fields.selected_sheet", { - name: - selectedIntegrationType === "googleSheets" - ? spreadsheets.find( - (s) => s.id === settings.spreadsheetId - )?.name - : airtableBases.find( - (b) => b.id === settings.airtableBaseId - )?.name, - id: - selectedIntegrationType === "googleSheets" - ? settings.spreadsheetId - : settings.airtableBaseId, - })} - + {!recording?.google_sheet_email ? ( + <> +

{t("integration_settings.descriptions.sync_info")}

+ + + ) : ( + <> + + {t("integration_settings.descriptions.authenticated_as", { + email: recording.google_sheet_email, + })} + + {loading ? ( + + ) : error ? ( + {error} + ) : spreadsheets.length === 0 ? ( + + ) : ( + <> + + {spreadsheets.map((sheet) => ( + + {sheet.name} + + ))} + + + + )} + )} + + )} + + )} + {/* Airtable Integration */} + {settings.integrationType === "airtable" && ( + <> + {recording?.airtable_base_id ? ( + <> + + {t("integration_settings.alerts.airtable_success.title")} + {t("integration_settings.alerts.airtable_success.content", { + baseName: recording.airtable_base_name, + })} + + {t("integration_settings.alerts.airtable_success.here")} + + + ) : ( + <> + {!recording?.airtable_access_token ? ( + <> +

{t("integration_settings.descriptions.airtable_sync_info")}

+ + + ) : ( + <> + + {t("integration_settings.descriptions.authenticated_as", { + email: "hghghg", + })} + + {loading ? ( + + ) : error ? ( + {error} + ) : airtableBases.length === 0 ? ( + + ) : ( + <> + + {airtableBases.map((base) => ( + + {base.name} + + ))} + + + + )} + + )} + )} )} @@ -570,4 +542,4 @@ export const modalStyle = { height: "fit-content", display: "block", padding: "20px", -}; +}; \ No newline at end of file