Merge pull request #84 from ritikprajapat21/main

Fix #45: Added connector icon
This commit is contained in:
Rohan Verma 2025-05-09 22:32:15 -07:00 committed by GitHub
commit 7498c1e6fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 467 additions and 387 deletions

View file

@ -4,256 +4,308 @@ import { useState, useEffect } from "react";
import { useRouter, useParams } from "next/navigation";
import { motion } from "framer-motion";
import { toast } from "sonner";
import { Edit, Plus, Search, Trash2, ExternalLink, RefreshCw } from "lucide-react";
import {
Edit,
Plus,
Search,
Trash2,
ExternalLink,
RefreshCw,
} from "lucide-react";
import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { getConnectorIcon } from "@/components/chat";
// Helper function to get connector type display name
const getConnectorTypeDisplay = (type: string): string => {
const typeMap: Record<string, string> = {
"SERPER_API": "Serper API",
"TAVILY_API": "Tavily API",
"SLACK_CONNECTOR": "Slack",
"NOTION_CONNECTOR": "Notion",
"GITHUB_CONNECTOR": "GitHub",
"LINEAR_CONNECTOR": "Linear",
"LINKUP_API": "Linkup",
// Add other connector types here as needed
};
return typeMap[type] || type;
const typeMap: Record<string, string> = {
SERPER_API: "Serper API",
TAVILY_API: "Tavily API",
SLACK_CONNECTOR: "Slack",
NOTION_CONNECTOR: "Notion",
GITHUB_CONNECTOR: "GitHub",
LINEAR_CONNECTOR: "Linear",
LINKUP_API: "Linkup",
// Add other connector types here as needed
};
return typeMap[type] || type;
};
// Helper function to format date with time
const formatDateTime = (dateString: string | null): string => {
if (!dateString) return "Never";
const date = new Date(dateString);
return new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
}).format(date);
if (!dateString) return "Never";
const date = new Date(dateString);
return new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
}).format(date);
};
export default function ConnectorsPage() {
const router = useRouter();
const params = useParams();
const searchSpaceId = params.search_space_id as string;
const { connectors, isLoading, error, deleteConnector, indexConnector } = useSearchSourceConnectors();
const [connectorToDelete, setConnectorToDelete] = useState<number | null>(null);
const [indexingConnectorId, setIndexingConnectorId] = useState<number | null>(null);
const router = useRouter();
const params = useParams();
const searchSpaceId = params.search_space_id as string;
useEffect(() => {
if (error) {
toast.error("Failed to load connectors");
console.error("Error fetching connectors:", error);
}
}, [error]);
const { connectors, isLoading, error, deleteConnector, indexConnector } =
useSearchSourceConnectors();
const [connectorToDelete, setConnectorToDelete] = useState<number | null>(
null,
);
const [indexingConnectorId, setIndexingConnectorId] = useState<number | null>(
null,
);
// Handle connector deletion
const handleDeleteConnector = async () => {
if (connectorToDelete === null) return;
try {
await deleteConnector(connectorToDelete);
toast.success("Connector deleted successfully");
} catch (error) {
console.error("Error deleting connector:", error);
toast.error("Failed to delete connector");
} finally {
setConnectorToDelete(null);
}
};
useEffect(() => {
if (error) {
toast.error("Failed to load connectors");
console.error("Error fetching connectors:", error);
}
}, [error]);
// Handle connector indexing
const handleIndexConnector = async (connectorId: number) => {
setIndexingConnectorId(connectorId);
try {
await indexConnector(connectorId, searchSpaceId);
toast.success("Connector content indexed successfully");
} catch (error) {
console.error("Error indexing connector content:", error);
toast.error(error instanceof Error ? error.message : "Failed to index connector content");
} finally {
setIndexingConnectorId(null);
}
};
// Handle connector deletion
const handleDeleteConnector = async () => {
if (connectorToDelete === null) return;
return (
<div className="container mx-auto py-8 max-w-6xl">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="mb-8 flex items-center justify-between"
>
<div>
<h1 className="text-3xl font-bold tracking-tight">Connectors</h1>
<p className="text-muted-foreground mt-2">
Manage your connected services and data sources.
</p>
</div>
<Button onClick={() => router.push(`/dashboard/${searchSpaceId}/connectors/add`)}>
<Plus className="mr-2 h-4 w-4" />
Add Connector
</Button>
</motion.div>
try {
await deleteConnector(connectorToDelete);
toast.success("Connector deleted successfully");
} catch (error) {
console.error("Error deleting connector:", error);
toast.error("Failed to delete connector");
} finally {
setConnectorToDelete(null);
}
};
<Card>
<CardHeader className="pb-3">
<CardTitle>Your Connectors</CardTitle>
<CardDescription>
View and manage all your connected services.
</CardDescription>
</CardHeader>
<CardContent>
{isLoading ? (
<div className="flex justify-center py-8">
<div className="animate-pulse text-center">
<div className="h-6 w-32 bg-muted rounded mx-auto mb-2"></div>
<div className="h-4 w-48 bg-muted rounded mx-auto"></div>
</div>
</div>
) : connectors.length === 0 ? (
<div className="text-center py-12">
<h3 className="text-lg font-medium mb-2">No connectors found</h3>
<p className="text-muted-foreground mb-6">
You haven't added any connectors yet. Add one to enhance your search capabilities.
</p>
<Button onClick={() => router.push(`/dashboard/${searchSpaceId}/connectors/add`)}>
<Plus className="mr-2 h-4 w-4" />
Add Your First Connector
</Button>
</div>
) : (
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Type</TableHead>
<TableHead>Last Indexed</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{connectors.map((connector) => (
<TableRow key={connector.id}>
<TableCell className="font-medium">{connector.name}</TableCell>
<TableCell>{getConnectorTypeDisplay(connector.connector_type)}</TableCell>
<TableCell>
{connector.is_indexable
? formatDateTime(connector.last_indexed_at)
: "Not indexable"}
</TableCell>
<TableCell className="text-right">
<div className="flex justify-end gap-2">
{connector.is_indexable && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="sm"
onClick={() => handleIndexConnector(connector.id)}
disabled={indexingConnectorId === connector.id}
>
{indexingConnectorId === connector.id ? (
<RefreshCw className="h-4 w-4 animate-spin" />
) : (
<RefreshCw className="h-4 w-4" />
)}
<span className="sr-only">Index Content</span>
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Index Content</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
<Button
variant="outline"
size="sm"
onClick={() => router.push(`/dashboard/${searchSpaceId}/connectors/${connector.id}/edit`)}
>
<Edit className="h-4 w-4" />
<span className="sr-only">Edit</span>
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="text-destructive-foreground hover:bg-destructive/10"
onClick={() => setConnectorToDelete(connector.id)}
>
<Trash2 className="h-4 w-4" />
<span className="sr-only">Delete</span>
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete Connector</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to delete this connector? This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={() => setConnectorToDelete(null)}>
Cancel
</AlertDialogCancel>
<AlertDialogAction
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
onClick={handleDeleteConnector}
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)}
</CardContent>
</Card>
</div>
);
}
// Handle connector indexing
const handleIndexConnector = async (connectorId: number) => {
setIndexingConnectorId(connectorId);
try {
await indexConnector(connectorId, searchSpaceId);
toast.success("Connector content indexed successfully");
} catch (error) {
console.error("Error indexing connector content:", error);
toast.error(
error instanceof Error
? error.message
: "Failed to index connector content",
);
} finally {
setIndexingConnectorId(null);
}
};
return (
<div className="container mx-auto py-8 max-w-6xl">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="mb-8 flex items-center justify-between"
>
<div>
<h1 className="text-3xl font-bold tracking-tight">Connectors</h1>
<p className="text-muted-foreground mt-2">
Manage your connected services and data sources.
</p>
</div>
<Button
onClick={() =>
router.push(`/dashboard/${searchSpaceId}/connectors/add`)
}
>
<Plus className="mr-2 h-4 w-4" />
Add Connector
</Button>
</motion.div>
<Card>
<CardHeader className="pb-3">
<CardTitle>Your Connectors</CardTitle>
<CardDescription>
View and manage all your connected services.
</CardDescription>
</CardHeader>
<CardContent>
{isLoading ? (
<div className="flex justify-center py-8">
<div className="animate-pulse text-center">
<div className="h-6 w-32 bg-muted rounded mx-auto mb-2"></div>
<div className="h-4 w-48 bg-muted rounded mx-auto"></div>
</div>
</div>
) : connectors.length === 0 ? (
<div className="text-center py-12">
<h3 className="text-lg font-medium mb-2">No connectors found</h3>
<p className="text-muted-foreground mb-6">
You haven't added any connectors yet. Add one to enhance your
search capabilities.
</p>
<Button
onClick={() =>
router.push(`/dashboard/${searchSpaceId}/connectors/add`)
}
>
<Plus className="mr-2 h-4 w-4" />
Add Your First Connector
</Button>
</div>
) : (
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Type</TableHead>
<TableHead>Last Indexed</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{connectors.map((connector) => (
<TableRow key={connector.id}>
<TableCell className="font-medium">
{connector.name}
</TableCell>
<TableCell>
{getConnectorIcon(connector.connector_type)}
</TableCell>
<TableCell>
{connector.is_indexable
? formatDateTime(connector.last_indexed_at)
: "Not indexable"}
</TableCell>
<TableCell className="text-right">
<div className="flex justify-end gap-2">
{connector.is_indexable && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="sm"
onClick={() =>
handleIndexConnector(connector.id)
}
disabled={
indexingConnectorId === connector.id
}
>
{indexingConnectorId === connector.id ? (
<RefreshCw className="h-4 w-4 animate-spin" />
) : (
<RefreshCw className="h-4 w-4" />
)}
<span className="sr-only">
Index Content
</span>
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Index Content</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
<Button
variant="outline"
size="sm"
onClick={() =>
router.push(
`/dashboard/${searchSpaceId}/connectors/${connector.id}/edit`,
)
}
>
<Edit className="h-4 w-4" />
<span className="sr-only">Edit</span>
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="text-destructive-foreground hover:bg-destructive/10"
onClick={() =>
setConnectorToDelete(connector.id)
}
>
<Trash2 className="h-4 w-4" />
<span className="sr-only">Delete</span>
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Delete Connector
</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to delete this
connector? This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel
onClick={() => setConnectorToDelete(null)}
>
Cancel
</AlertDialogCancel>
<AlertDialogAction
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
onClick={handleDeleteConnector}
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)}
</CardContent>
</Card>
</div>
);
}

View file

@ -1,6 +1,6 @@
"use client";
import React, { useEffect } from 'react';
import React, { useEffect } from "react";
import { useRouter, useParams } from "next/navigation";
import { motion } from "framer-motion";
import { toast } from "sonner";
@ -8,180 +8,208 @@ import { ArrowLeft, Check, Loader2, Github } from "lucide-react";
import { Form } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
// Import Utils, Types, Hook, and Components
import { getConnectorTypeDisplay } from '@/lib/connectors/utils';
import { useConnectorEditPage } from '@/hooks/useConnectorEditPage';
import { getConnectorTypeDisplay } from "@/lib/connectors/utils";
import { useConnectorEditPage } from "@/hooks/useConnectorEditPage";
import { EditConnectorLoadingSkeleton } from "@/components/editConnector/EditConnectorLoadingSkeleton";
import { EditConnectorNameForm } from "@/components/editConnector/EditConnectorNameForm";
import { EditGitHubConnectorConfig } from "@/components/editConnector/EditGitHubConnectorConfig";
import { EditSimpleTokenForm } from "@/components/editConnector/EditSimpleTokenForm";
import { getConnectorIcon } from "@/components/chat";
export default function EditConnectorPage() {
const router = useRouter();
const params = useParams();
const searchSpaceId = params.search_space_id as string;
// Ensure connectorId is parsed safely
const connectorIdParam = params.connector_id as string;
const connectorId = connectorIdParam ? parseInt(connectorIdParam, 10) : NaN;
const router = useRouter();
const params = useParams();
const searchSpaceId = params.search_space_id as string;
// Ensure connectorId is parsed safely
const connectorIdParam = params.connector_id as string;
const connectorId = connectorIdParam ? parseInt(connectorIdParam, 10) : NaN;
// Use the custom hook to manage state and logic
const {
connectorsLoading,
connector,
isSaving,
editForm,
patForm, // Needed for GitHub child component
handleSaveChanges,
// GitHub specific props for the child component
editMode,
setEditMode, // Pass down if needed by GitHub component
originalPat,
currentSelectedRepos,
fetchedRepos,
setFetchedRepos,
newSelectedRepos,
setNewSelectedRepos,
isFetchingRepos,
handleFetchRepositories,
handleRepoSelectionChange,
} = useConnectorEditPage(connectorId, searchSpaceId);
// Use the custom hook to manage state and logic
const {
connectorsLoading,
connector,
isSaving,
editForm,
patForm, // Needed for GitHub child component
handleSaveChanges,
// GitHub specific props for the child component
editMode,
setEditMode, // Pass down if needed by GitHub component
originalPat,
currentSelectedRepos,
fetchedRepos,
setFetchedRepos,
newSelectedRepos,
setNewSelectedRepos,
isFetchingRepos,
handleFetchRepositories,
handleRepoSelectionChange,
} = useConnectorEditPage(connectorId, searchSpaceId);
// Redirect if connectorId is not a valid number after parsing
useEffect(() => {
if (isNaN(connectorId)) {
toast.error("Invalid Connector ID.");
router.push(`/dashboard/${searchSpaceId}/connectors`);
}
}, [connectorId, router, searchSpaceId]);
// Redirect if connectorId is not a valid number after parsing
useEffect(() => {
if (isNaN(connectorId)) {
toast.error("Invalid Connector ID.");
router.push(`/dashboard/${searchSpaceId}/connectors`);
}
}, [connectorId, router, searchSpaceId]);
// Loading State
if (connectorsLoading || !connector) {
// Handle NaN case before showing skeleton
if (isNaN(connectorId)) return null;
return <EditConnectorLoadingSkeleton />;
}
// Loading State
if (connectorsLoading || !connector) {
// Handle NaN case before showing skeleton
if (isNaN(connectorId)) return null;
return <EditConnectorLoadingSkeleton />;
}
// Main Render using data/handlers from the hook
return (
<div className="container mx-auto py-8 max-w-3xl">
<Button variant="ghost" className="mb-6" onClick={() => router.push(`/dashboard/${searchSpaceId}/connectors`)}>
<ArrowLeft className="mr-2 h-4 w-4" /> Back to Connectors
</Button>
// Main Render using data/handlers from the hook
return (
<div className="container mx-auto py-8 max-w-3xl">
<Button
variant="ghost"
className="mb-6"
onClick={() => router.push(`/dashboard/${searchSpaceId}/connectors`)}
>
<ArrowLeft className="mr-2 h-4 w-4" /> Back to Connectors
</Button>
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
<Card className="border-2 border-border">
<CardHeader>
<CardTitle className="text-2xl font-bold flex items-center gap-2">
<Github className="h-6 w-6" /> {/* TODO: Dynamic icon */}
Edit {getConnectorTypeDisplay(connector.connector_type)} Connector
</CardTitle>
<CardDescription>Modify connector name and configuration.</CardDescription>
</CardHeader>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<Card className="border-2 border-border">
<CardHeader>
<CardTitle className="text-2xl font-bold flex items-center gap-2">
{getConnectorIcon(connector.connector_type)}
Edit {getConnectorTypeDisplay(connector.connector_type)} Connector
</CardTitle>
<CardDescription>
Modify connector name and configuration.
</CardDescription>
</CardHeader>
<Form {...editForm}>
{/* Pass hook's handleSaveChanges */}
<form onSubmit={editForm.handleSubmit(handleSaveChanges)} className="space-y-6">
<CardContent className="space-y-6">
{/* Pass form control from hook */}
<EditConnectorNameForm control={editForm.control} />
<Form {...editForm}>
{/* Pass hook's handleSaveChanges */}
<form
onSubmit={editForm.handleSubmit(handleSaveChanges)}
className="space-y-6"
>
<CardContent className="space-y-6">
{/* Pass form control from hook */}
<EditConnectorNameForm control={editForm.control} />
<hr />
<hr />
<h3 className="text-lg font-semibold">Configuration</h3>
<h3 className="text-lg font-semibold">Configuration</h3>
{/* == GitHub == */}
{connector.connector_type === 'GITHUB_CONNECTOR' && (
<EditGitHubConnectorConfig
// Pass relevant state and handlers from hook
editMode={editMode}
setEditMode={setEditMode} // Pass setter if child manages mode
originalPat={originalPat}
currentSelectedRepos={currentSelectedRepos}
fetchedRepos={fetchedRepos}
newSelectedRepos={newSelectedRepos}
isFetchingRepos={isFetchingRepos}
patForm={patForm}
handleFetchRepositories={handleFetchRepositories}
handleRepoSelectionChange={handleRepoSelectionChange}
setNewSelectedRepos={setNewSelectedRepos}
setFetchedRepos={setFetchedRepos}
/>
)}
{/* == GitHub == */}
{connector.connector_type === "GITHUB_CONNECTOR" && (
<EditGitHubConnectorConfig
// Pass relevant state and handlers from hook
editMode={editMode}
setEditMode={setEditMode} // Pass setter if child manages mode
originalPat={originalPat}
currentSelectedRepos={currentSelectedRepos}
fetchedRepos={fetchedRepos}
newSelectedRepos={newSelectedRepos}
isFetchingRepos={isFetchingRepos}
patForm={patForm}
handleFetchRepositories={handleFetchRepositories}
handleRepoSelectionChange={handleRepoSelectionChange}
setNewSelectedRepos={setNewSelectedRepos}
setFetchedRepos={setFetchedRepos}
/>
)}
{/* == Slack == */}
{connector.connector_type === 'SLACK_CONNECTOR' && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="SLACK_BOT_TOKEN"
fieldLabel="Slack Bot Token"
fieldDescription="Update the Slack Bot Token if needed."
placeholder="Begins with xoxb-..."
/>
)}
{/* == Notion == */}
{connector.connector_type === 'NOTION_CONNECTOR' && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="NOTION_INTEGRATION_TOKEN"
fieldLabel="Notion Integration Token"
fieldDescription="Update the Notion Integration Token if needed."
placeholder="Begins with secret_..."
/>
)}
{/* == Serper == */}
{connector.connector_type === 'SERPER_API' && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="SERPER_API_KEY"
fieldLabel="Serper API Key"
fieldDescription="Update the Serper API Key if needed."
/>
)}
{/* == Tavily == */}
{connector.connector_type === 'TAVILY_API' && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="TAVILY_API_KEY"
fieldLabel="Tavily API Key"
fieldDescription="Update the Tavily API Key if needed."
/>
)}
{/* == Slack == */}
{connector.connector_type === "SLACK_CONNECTOR" && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="SLACK_BOT_TOKEN"
fieldLabel="Slack Bot Token"
fieldDescription="Update the Slack Bot Token if needed."
placeholder="Begins with xoxb-..."
/>
)}
{/* == Notion == */}
{connector.connector_type === "NOTION_CONNECTOR" && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="NOTION_INTEGRATION_TOKEN"
fieldLabel="Notion Integration Token"
fieldDescription="Update the Notion Integration Token if needed."
placeholder="Begins with secret_..."
/>
)}
{/* == Serper == */}
{connector.connector_type === "SERPER_API" && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="SERPER_API_KEY"
fieldLabel="Serper API Key"
fieldDescription="Update the Serper API Key if needed."
/>
)}
{/* == Tavily == */}
{connector.connector_type === "TAVILY_API" && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="TAVILY_API_KEY"
fieldLabel="Tavily API Key"
fieldDescription="Update the Tavily API Key if needed."
/>
)}
{/* == Linear == */}
{connector.connector_type === 'LINEAR_CONNECTOR' && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="LINEAR_API_KEY"
fieldLabel="Linear API Key"
fieldDescription="Update your Linear API Key if needed."
placeholder="Begins with lin_api_..."
/>
)}
{/* == Linear == */}
{connector.connector_type === "LINEAR_CONNECTOR" && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="LINEAR_API_KEY"
fieldLabel="Linear API Key"
fieldDescription="Update your Linear API Key if needed."
placeholder="Begins with lin_api_..."
/>
)}
{/* == Linkup == */}
{connector.connector_type === 'LINKUP_API' && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="LINKUP_API_KEY"
fieldLabel="Linkup API Key"
fieldDescription="Update your Linkup API Key if needed."
placeholder="Begins with linkup_..."
/>
)}
</CardContent>
<CardFooter className="border-t pt-6">
<Button type="submit" disabled={isSaving} className="w-full sm:w-auto">
{isSaving ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Check className="mr-2 h-4 w-4" />}
Save Changes
</Button>
</CardFooter>
</form>
</Form>
</Card>
</motion.div>
</div>
);
}
{/* == Linkup == */}
{connector.connector_type === "LINKUP_API" && (
<EditSimpleTokenForm
control={editForm.control}
fieldName="LINKUP_API_KEY"
fieldLabel="Linkup API Key"
fieldDescription="Update your Linkup API Key if needed."
placeholder="Begins with linkup_..."
/>
)}
</CardContent>
<CardFooter className="border-t pt-6">
<Button
type="submit"
disabled={isSaving}
className="w-full sm:w-auto"
>
{isSaving ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<Check className="mr-2 h-4 w-4" />
)}
Save Changes
</Button>
</CardFooter>
</form>
</Form>
</Card>
</motion.div>
</div>
);
}