import { useState } from "react"; import { json } from "@remix-run/node"; import { useLoaderData } from "@remix-run/react"; import { type LoaderFunctionArgs } from "@remix-run/server-runtime"; import { requireUserId, requireWorkpace } from "~/services/session.server"; import { getIntegrationDefinitions } from "~/services/integrationDefinition.server"; import { getIntegrationAccounts } from "~/services/integrationAccount.server"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"; import { Button } from "~/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"; import { Input } from "~/components/ui/input"; import { FormButtons } from "~/components/ui/FormButtons"; import { Plus, Search } from "lucide-react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"; // Loader to fetch integration definitions and existing accounts export async function loader({ request }: LoaderFunctionArgs) { const userId = await requireUserId(request); const workspace = await requireWorkpace(request); const [integrationDefinitions, integrationAccounts] = await Promise.all([ getIntegrationDefinitions(workspace.id), getIntegrationAccounts(userId), ]); return json({ integrationDefinitions, integrationAccounts, userId, }); } export default function Integrations() { const { integrationDefinitions, integrationAccounts, userId } = useLoaderData(); const [selectedCategory, setSelectedCategory] = useState("all"); const [selectedIntegration, setSelectedIntegration] = useState(null); const [apiKey, setApiKey] = useState(""); const [isLoading, setIsLoading] = useState(false); const [isConnecting, setIsConnecting] = useState(false); // Extract categories from integration definitions const categories = Array.from( new Set(integrationDefinitions.map((integration) => { const specData = typeof integration.spec === 'string' ? JSON.parse(integration.spec) : integration.spec; return specData?.category || "Uncategorized"; })) ); // Filter integrations by selected category const filteredIntegrations = selectedCategory === "all" ? integrationDefinitions : integrationDefinitions.filter( (integration) => { const specData = typeof integration.spec === 'string' ? JSON.parse(integration.spec) : integration.spec; return specData?.category === selectedCategory; } ); // Check if user has an active account for an integration const hasActiveAccount = (integrationDefinitionId: string) => { return integrationAccounts.some( (account) => account.integrationDefinitionId === integrationDefinitionId && account.isActive ); }; // Handle connection with API key const handleApiKeyConnect = async () => { if (!selectedIntegration || !apiKey.trim()) return; setIsLoading(true); try { const response = await fetch("/api/v1/integration_account", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ integrationDefinitionId: selectedIntegration.id, apiKey, }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || "Failed to connect integration"); } // Refresh the page to show the new integration account window.location.reload(); } catch (error) { console.error("Error connecting integration:", error); // Handle error (could add error state and display message) } finally { setIsLoading(false); } }; // Handle OAuth connection const handleOAuthConnect = async () => { if (!selectedIntegration) return; setIsConnecting(true); try { const response = await fetch("/api/v1/oauth", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ integrationDefinitionId: selectedIntegration.id, userId, }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || "Failed to start OAuth flow"); } const { url } = await response.json(); // Redirect to OAuth authorization URL window.location.href = url; } catch (error) { console.error("Error starting OAuth flow:", error); // Handle error } finally { setIsConnecting(false); } }; return (

Integrations

Connect your tools and services

{/* Category filter */} {/* Add integration button */}
{/* Integration cards grid */}
{filteredIntegrations.map((integration) => { const isConnected = hasActiveAccount(integration.id); const authType = integration.spec?.auth?.type || "unknown"; return ( { if (open) { setSelectedIntegration(integration); setApiKey(""); } else { setSelectedIntegration(null); } }}>
{integration.icon ? ( {integration.name} ) : (
)}
{integration.name} {integration.description || "Connect to " + integration.name}
{(() => { const specData = typeof integration.spec === 'string' ? JSON.parse(integration.spec) : integration.spec; return specData?.category || "Uncategorized"; })()} {isConnected ? ( Connected ) : ( Not connected )}
Connect to {integration.name} {integration.description || `Connect your ${integration.name} account to enable integration.`} {/* API Key Authentication */} {(() => { const specData = typeof integration.spec === 'string' ? JSON.parse(integration.spec) : integration.spec; return specData?.auth?.api_key; })() && (
setApiKey(e.target.value)} /> {(() => { const specData = typeof integration.spec === 'string' ? JSON.parse(integration.spec) : integration.spec; return specData?.auth?.api_key?.description; })() && (

{(() => { const specData = typeof integration.spec === 'string' ? JSON.parse(integration.spec) : integration.spec; return specData?.auth?.api_key?.description; })()}

)}
)} {/* OAuth Authentication */} {(() => { const specData = typeof integration.spec === 'string' ? JSON.parse(integration.spec) : integration.spec; return specData?.auth?.oauth2; })() && (
)} {/* No authentication method found */} {(() => { const specData = typeof integration.spec === 'string' ? JSON.parse(integration.spec) : integration.spec; return !specData?.auth?.api_key && !specData?.auth?.oauth2; })() && (
This integration doesn't specify an authentication method.
)}
By connecting, you agree to the {integration.name} terms of service.
); })}
{/* Empty state */} {filteredIntegrations.length === 0 && (

No integrations found

{selectedCategory === "all" ? "No integrations are available at this time." : `No integrations found in the "${selectedCategory}" category.`}

)}
); }