Merge pull request #594 from CREDO23/feat/add-jotai-tanstack-connectors

[Feat] Connectors| Add jotai & tanstack
This commit is contained in:
Rohan Verma 2025-12-26 21:43:11 -08:00 committed by GitHub
commit 2c2ae40274
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 3765 additions and 3322 deletions

5981
surfsense_backend/uv.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { format } from "date-fns"; import { format } from "date-fns";
import { useAtomValue } from "jotai";
import { import {
Calendar as CalendarIcon, Calendar as CalendarIcon,
Clock, Clock,
@ -15,6 +16,12 @@ import { useParams, useRouter } from "next/navigation";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import {
deleteConnectorMutationAtom,
indexConnectorMutationAtom,
updateConnectorMutationAtom,
} from "@/atoms/connectors/connector-mutation.atoms";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
@ -59,7 +66,6 @@ import {
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export default function ConnectorsPage() { export default function ConnectorsPage() {
@ -84,8 +90,12 @@ export default function ConnectorsPage() {
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const today = new Date(); const today = new Date();
const { connectors, isLoading, error, deleteConnector, indexConnector, updateConnector } = const { data: connectors = [], isLoading, error } = useAtomValue(connectorsAtom);
useSearchSourceConnectors(false, parseInt(searchSpaceId));
const { mutateAsync: deleteConnector } = useAtomValue(deleteConnectorMutationAtom);
const { mutateAsync: indexConnector } = useAtomValue(indexConnectorMutationAtom);
const { mutateAsync: updateConnector } = useAtomValue(updateConnectorMutationAtom);
const [connectorToDelete, setConnectorToDelete] = useState<number | null>(null); const [connectorToDelete, setConnectorToDelete] = useState<number | null>(null);
const [indexingConnectorId, setIndexingConnectorId] = useState<number | null>(null); const [indexingConnectorId, setIndexingConnectorId] = useState<number | null>(null);
const [datePickerOpen, setDatePickerOpen] = useState(false); const [datePickerOpen, setDatePickerOpen] = useState(false);
@ -117,11 +127,9 @@ export default function ConnectorsPage() {
if (connectorToDelete === null) return; if (connectorToDelete === null) return;
try { try {
await deleteConnector(connectorToDelete); await deleteConnector({ id: connectorToDelete });
toast.success(t("delete_success"));
} catch (error) { } catch (error) {
console.error("Error deleting connector:", error); console.error("Error deleting connector:", error);
toast.error(t("delete_failed"));
} finally { } finally {
setConnectorToDelete(null); setConnectorToDelete(null);
} }
@ -144,7 +152,14 @@ export default function ConnectorsPage() {
const startDateStr = startDate ? format(startDate, "yyyy-MM-dd") : undefined; const startDateStr = startDate ? format(startDate, "yyyy-MM-dd") : undefined;
const endDateStr = endDate ? format(endDate, "yyyy-MM-dd") : undefined; const endDateStr = endDate ? format(endDate, "yyyy-MM-dd") : undefined;
await indexConnector(selectedConnectorForIndexing, searchSpaceId, startDateStr, endDateStr); await indexConnector({
connector_id: selectedConnectorForIndexing,
queryParams: {
search_space_id: searchSpaceId,
start_date: startDateStr,
end_date: endDateStr,
},
});
toast.success(t("indexing_started")); toast.success(t("indexing_started"));
} catch (error) { } catch (error) {
console.error("Error indexing connector content:", error); console.error("Error indexing connector content:", error);
@ -161,7 +176,12 @@ export default function ConnectorsPage() {
const handleQuickIndexConnector = async (connectorId: number) => { const handleQuickIndexConnector = async (connectorId: number) => {
setIndexingConnectorId(connectorId); setIndexingConnectorId(connectorId);
try { try {
await indexConnector(connectorId, searchSpaceId); await indexConnector({
connector_id: connectorId,
queryParams: {
search_space_id: searchSpaceId,
},
});
toast.success(t("indexing_started")); toast.success(t("indexing_started"));
} catch (error) { } catch (error) {
console.error("Error indexing connector content:", error); console.error("Error indexing connector content:", error);
@ -221,9 +241,12 @@ export default function ConnectorsPage() {
} }
} }
await updateConnector(selectedConnectorForPeriodic, { await updateConnector({
periodic_indexing_enabled: periodicEnabled, id: selectedConnectorForPeriodic,
indexing_frequency_minutes: frequency, data: {
periodic_indexing_enabled: periodicEnabled,
indexing_frequency_minutes: frequency,
},
}); });
toast.success( toast.success(

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,8 @@ import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { updateConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
@ -21,10 +24,8 @@ import {
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { import type { EnumConnectorName } from "@/contracts/enums/connector";
type SearchSourceConnector, import type { SearchSourceConnector } from "@/hooks/use-search-source-connectors";
useSearchSourceConnectors,
} from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const apiConnectorFormSchema = z.object({ const apiConnectorFormSchema = z.object({
@ -85,7 +86,8 @@ export default function EditConnectorPage() {
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const connectorId = parseInt(params.connector_id as string, 10); const connectorId = parseInt(params.connector_id as string, 10);
const { connectors, updateConnector } = useSearchSourceConnectors(false, parseInt(searchSpaceId)); const { data: connectors = [] } = useAtomValue(connectorsAtom);
const { mutateAsync: updateConnector } = useAtomValue(updateConnectorMutationAtom);
const [connector, setConnector] = useState<SearchSourceConnector | null>(null); const [connector, setConnector] = useState<SearchSourceConnector | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
@ -99,14 +101,12 @@ export default function EditConnectorPage() {
}, },
}); });
// Find connector in the list
useEffect(() => { useEffect(() => {
const currentConnector = connectors.find((c) => c.id === connectorId); const currentConnector = connectors.find((c) => c.id === connectorId);
if (currentConnector) { if (currentConnector) {
setConnector(currentConnector); setConnector(currentConnector);
// Check if connector type is supported
const apiKeyField = getApiKeyFieldName(currentConnector.connector_type); const apiKeyField = getApiKeyFieldName(currentConnector.connector_type);
if (apiKeyField) { if (apiKeyField) {
form.reset({ form.reset({
@ -114,14 +114,12 @@ export default function EditConnectorPage() {
api_key: currentConnector.config[apiKeyField] || "", api_key: currentConnector.config[apiKeyField] || "",
}); });
} else { } else {
// Redirect if not a supported connector type
toast.error("This connector type is not supported for editing"); toast.error("This connector type is not supported for editing");
router.push(`/dashboard/${searchSpaceId}/connectors`); router.push(`/dashboard/${searchSpaceId}/connectors`);
} }
setIsLoading(false); setIsLoading(false);
} else if (!isLoading && connectors.length > 0) { } else if (!isLoading && connectors.length > 0) {
// If connectors are loaded but this one isn't found
toast.error("Connector not found"); toast.error("Connector not found");
router.push(`/dashboard/${searchSpaceId}/connectors`); router.push(`/dashboard/${searchSpaceId}/connectors`);
} }
@ -135,18 +133,20 @@ export default function EditConnectorPage() {
try { try {
const apiKeyField = getApiKeyFieldName(connector.connector_type); const apiKeyField = getApiKeyFieldName(connector.connector_type);
// Only update the API key if a new one was provided
const updatedConfig = { ...connector.config }; const updatedConfig = { ...connector.config };
if (values.api_key) { if (values.api_key) {
updatedConfig[apiKeyField] = values.api_key; updatedConfig[apiKeyField] = values.api_key;
} }
await updateConnector(connectorId, { await updateConnector({
name: values.name, id: connectorId,
connector_type: connector.connector_type, data: {
config: updatedConfig, name: values.name,
is_indexable: connector.is_indexable, connector_type: connector.connector_type as EnumConnectorName,
last_indexed_at: connector.last_indexed_at, config: updatedConfig,
is_indexable: connector.is_indexable,
last_indexed_at: connector.last_indexed_at,
},
}); });
toast.success("Connector updated successfully!"); toast.success("Connector updated successfully!");

View file

@ -1,11 +1,13 @@
"use client"; "use client";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, ExternalLink, Loader2 } from "lucide-react"; import { ArrowLeft, Check, ExternalLink, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import Link from "next/link"; import Link from "next/link";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
@ -18,11 +20,8 @@ import {
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
// import { IconBrandAirtable } from "@tabler/icons-react"; // import { IconBrandAirtable } from "@tabler/icons-react";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import {
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/use-search-source-connectors";
import { authenticatedFetch } from "@/lib/auth-utils"; import { authenticatedFetch } from "@/lib/auth-utils";
import { SearchSourceConnector } from "@/contracts/types/connector.types";
export default function AirtableConnectorPage() { export default function AirtableConnectorPage() {
const router = useRouter(); const router = useRouter();
@ -31,11 +30,12 @@ export default function AirtableConnectorPage() {
const [isConnecting, setIsConnecting] = useState(false); const [isConnecting, setIsConnecting] = useState(false);
const [doesConnectorExist, setDoesConnectorExist] = useState(false); const [doesConnectorExist, setDoesConnectorExist] = useState(false);
const { fetchConnectors } = useSearchSourceConnectors(true, parseInt(searchSpaceId)); const { refetch : fetchConnectors } = useAtomValue(connectorsAtom);
useEffect(() => { useEffect(() => {
fetchConnectors(parseInt(searchSpaceId)).then((data) => { fetchConnectors().then((data) => {
const connector = data.find( const connectors = data.data || [];
const connector = connectors.find(
(c: SearchSourceConnector) => c.connector_type === EnumConnectorName.AIRTABLE_CONNECTOR (c: SearchSourceConnector) => c.connector_type === EnumConnectorName.AIRTABLE_CONNECTOR
); );
if (connector) { if (connector) {

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
@ -38,7 +40,6 @@ import {
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const baiduSearchApiFormSchema = z.object({ const baiduSearchApiFormSchema = z.object({
@ -61,7 +62,7 @@ export default function BaiduSearchApiPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<BaiduSearchApiFormValues>({ const form = useForm<BaiduSearchApiFormValues>({
@ -95,8 +96,8 @@ export default function BaiduSearchApiPage() {
config.BAIDU_ENABLE_DEEP_SEARCH = values.enable_deep_search; config.BAIDU_ENABLE_DEEP_SEARCH = values.enable_deep_search;
} }
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.BAIDU_SEARCH_API, connector_type: EnumConnectorName.BAIDU_SEARCH_API,
config, config,
@ -106,8 +107,10 @@ export default function BaiduSearchApiPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Baidu Search connector created successfully!"); toast.success("Baidu Search connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { Alert, AlertDescription } from "@/components/ui/alert"; import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
@ -24,7 +26,6 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const bookstackConnectorFormSchema = z.object({ const bookstackConnectorFormSchema = z.object({
@ -50,7 +51,7 @@ export default function BookStackConnectorPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<BookStackConnectorFormValues>({ const form = useForm<BookStackConnectorFormValues>({
@ -67,8 +68,8 @@ export default function BookStackConnectorPage() {
const onSubmit = async (values: BookStackConnectorFormValues) => { const onSubmit = async (values: BookStackConnectorFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.BOOKSTACK_CONNECTOR, connector_type: EnumConnectorName.BOOKSTACK_CONNECTOR,
config: { config: {
@ -82,8 +83,10 @@ export default function BookStackConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("BookStack connector created successfully!"); toast.success("BookStack connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, ExternalLink, Eye, EyeOff } from "lucide-react"; import { ArrowLeft, ExternalLink, Eye, EyeOff } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { import {
@ -22,7 +24,6 @@ import {
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const clickupConnectorFormSchema = z.object({ const clickupConnectorFormSchema = z.object({
@ -41,7 +42,7 @@ export default function ClickUpConnectorPage() {
const router = useRouter(); const router = useRouter();
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [showApiToken, setShowApiToken] = useState(false); const [showApiToken, setShowApiToken] = useState(false);
@ -59,20 +60,23 @@ export default function ClickUpConnectorPage() {
setIsLoading(true); setIsLoading(true);
try { try {
const connectorData = { await createConnector({
name: values.name, data: {
connector_type: EnumConnectorName.CLICKUP_CONNECTOR, name: values.name,
is_indexable: true, connector_type: EnumConnectorName.CLICKUP_CONNECTOR,
config: { is_indexable: true,
CLICKUP_API_TOKEN: values.api_token, config: {
CLICKUP_API_TOKEN: values.api_token,
},
last_indexed_at: null,
periodic_indexing_enabled: false,
indexing_frequency_minutes: null,
next_scheduled_at: null,
}, },
last_indexed_at: null, queryParams: {
periodic_indexing_enabled: false, search_space_id: searchSpaceId,
indexing_frequency_minutes: null, },
next_scheduled_at: null, });
};
await createConnector(connectorData, parseInt(searchSpaceId));
toast.success("ClickUp connector created successfully!"); toast.success("ClickUp connector created successfully!");
router.push(`/dashboard/${searchSpaceId}/connectors`); router.push(`/dashboard/${searchSpaceId}/connectors`);

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { Alert, AlertDescription } from "@/components/ui/alert"; import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
@ -24,7 +26,6 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const confluenceConnectorFormSchema = z.object({ const confluenceConnectorFormSchema = z.object({
@ -60,7 +61,7 @@ export default function ConfluenceConnectorPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<ConfluenceConnectorFormValues>({ const form = useForm<ConfluenceConnectorFormValues>({
@ -77,8 +78,8 @@ export default function ConfluenceConnectorPage() {
const onSubmit = async (values: ConfluenceConnectorFormValues) => { const onSubmit = async (values: ConfluenceConnectorFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.CONFLUENCE_CONNECTOR, connector_type: EnumConnectorName.CONFLUENCE_CONNECTOR,
config: { config: {
@ -92,8 +93,10 @@ export default function ConfluenceConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Confluence connector created successfully!"); toast.success("Confluence connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { import {
Accordion, Accordion,
AccordionContent, AccordionContent,
@ -37,7 +39,6 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const discordConnectorFormSchema = z.object({ const discordConnectorFormSchema = z.object({
@ -58,7 +59,7 @@ export default function DiscordConnectorPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<DiscordConnectorFormValues>({ const form = useForm<DiscordConnectorFormValues>({
@ -73,8 +74,8 @@ export default function DiscordConnectorPage() {
const onSubmit = async (values: DiscordConnectorFormValues) => { const onSubmit = async (values: DiscordConnectorFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.DISCORD_CONNECTOR, connector_type: EnumConnectorName.DISCORD_CONNECTOR,
config: { config: {
@ -86,8 +87,10 @@ export default function DiscordConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Discord connector created successfully!"); toast.success("Discord connector created successfully!");
router.push(`/dashboard/${searchSpaceId}/connectors`); router.push(`/dashboard/${searchSpaceId}/connectors`);

View file

@ -2,6 +2,7 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import * as RadioGroup from "@radix-ui/react-radio-group"; import * as RadioGroup from "@radix-ui/react-radio-group";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useParams, useRouter, useSearchParams } from "next/navigation";
@ -9,7 +10,7 @@ import { useId, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { import {
Accordion, Accordion,
AccordionContent, AccordionContent,
@ -40,10 +41,8 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const elasticsearchConnectorFormSchema = z const elasticsearchConnectorFormSchema = z
@ -91,7 +90,7 @@ export default function ElasticsearchConnectorPage() {
const authBasicId = useId(); const authBasicId = useId();
const authApiKeyId = useId(); const authApiKeyId = useId();
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<ElasticsearchConnectorFormValues>({ const form = useForm<ElasticsearchConnectorFormValues>({
@ -173,19 +172,21 @@ export default function ElasticsearchConnectorPage() {
config.ELASTICSEARCH_MAX_DOCUMENTS = values.max_documents; config.ELASTICSEARCH_MAX_DOCUMENTS = values.max_documents;
} }
const connectorPayload = { await createConnector({
name: values.name, data: {
connector_type: EnumConnectorName.ELASTICSEARCH_CONNECTOR, name: values.name,
is_indexable: true, connector_type: EnumConnectorName.ELASTICSEARCH_CONNECTOR,
last_indexed_at: null, is_indexable: true,
periodic_indexing_enabled: false, last_indexed_at: null,
indexing_frequency_minutes: null, periodic_indexing_enabled: false,
next_scheduled_at: null, indexing_frequency_minutes: null,
config, next_scheduled_at: null,
}; config,
},
// Use existing hook method queryParams: {
await createConnector(connectorPayload, searchSpaceIdNum); search_space_id: searchSpaceId,
},
});
toast.success("Elasticsearch connector created successfully!"); toast.success("Elasticsearch connector created successfully!");
router.push(`/dashboard/${searchSpaceId}/connectors`); router.push(`/dashboard/${searchSpaceId}/connectors`);

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, CircleAlert, Github, Info, ListChecks, Loader2 } from "lucide-react"; import { ArrowLeft, Check, CircleAlert, Github, Info, ListChecks, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { import {
Accordion, Accordion,
AccordionContent, AccordionContent,
@ -38,8 +40,6 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
// Assuming useSearchSourceConnectors hook exists and works similarly
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
import { authenticatedFetch, redirectToLogin } from "@/lib/auth-utils"; import { authenticatedFetch, redirectToLogin } from "@/lib/auth-utils";
// Define the form schema with Zod for GitHub PAT entry step // Define the form schema with Zod for GitHub PAT entry step
@ -85,7 +85,7 @@ export default function GithubConnectorPage() {
const [connectorName, setConnectorName] = useState<string>("GitHub Connector"); const [connectorName, setConnectorName] = useState<string>("GitHub Connector");
const [validatedPat, setValidatedPat] = useState<string>(""); // Store the validated PAT const [validatedPat, setValidatedPat] = useState<string>(""); // Store the validated PAT
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form for PAT entry // Initialize the form for PAT entry
const form = useForm<GithubPatFormValues>({ const form = useForm<GithubPatFormValues>({
@ -141,8 +141,8 @@ export default function GithubConnectorPage() {
setIsCreatingConnector(true); setIsCreatingConnector(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: connectorName, // Use the stored name name: connectorName, // Use the stored name
connector_type: EnumConnectorName.GITHUB_CONNECTOR, connector_type: EnumConnectorName.GITHUB_CONNECTOR,
config: { config: {
@ -155,8 +155,10 @@ export default function GithubConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("GitHub connector created successfully!"); toast.success("GitHub connector created successfully!");
router.push(`/dashboard/${searchSpaceId}/connectors`); router.push(`/dashboard/${searchSpaceId}/connectors`);

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, ExternalLink, Loader2 } from "lucide-react"; import { ArrowLeft, Check, ExternalLink, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import Link from "next/link"; import Link from "next/link";
@ -9,6 +10,7 @@ import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
@ -20,11 +22,8 @@ import {
} from "@/components/ui/card"; } from "@/components/ui/card";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import {
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/use-search-source-connectors";
import { authenticatedFetch } from "@/lib/auth-utils"; import { authenticatedFetch } from "@/lib/auth-utils";
import { SearchSourceConnector } from "@/contracts/types/connector.types";
export default function GoogleCalendarConnectorPage() { export default function GoogleCalendarConnectorPage() {
const router = useRouter(); const router = useRouter();
@ -33,13 +32,13 @@ export default function GoogleCalendarConnectorPage() {
const [isConnecting, setIsConnecting] = useState(false); const [isConnecting, setIsConnecting] = useState(false);
const [doesConnectorExist, setDoesConnectorExist] = useState(false); const [doesConnectorExist, setDoesConnectorExist] = useState(false);
const { fetchConnectors } = useSearchSourceConnectors(true, parseInt(searchSpaceId)); const { refetch : fetchConnectors } = useAtomValue(connectorsAtom);
useEffect(() => { useEffect(() => {
fetchConnectors(parseInt(searchSpaceId)).then((data) => { fetchConnectors().then((data) => {
const connector = data.find( const connectors = data.data || [];
(c: SearchSourceConnector) => const connector = connectors.find(
c.connector_type === EnumConnectorName.GOOGLE_CALENDAR_CONNECTOR (c: SearchSourceConnector) => c.connector_type === EnumConnectorName.GOOGLE_CALENDAR_CONNECTOR
); );
if (connector) { if (connector) {
setDoesConnectorExist(true); setDoesConnectorExist(true);

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, ExternalLink, Loader2 } from "lucide-react"; import { ArrowLeft, Check, ExternalLink, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import Link from "next/link"; import Link from "next/link";
@ -9,6 +10,7 @@ import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
@ -20,11 +22,8 @@ import {
} from "@/components/ui/card"; } from "@/components/ui/card";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import {
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/use-search-source-connectors";
import { authenticatedFetch } from "@/lib/auth-utils"; import { authenticatedFetch } from "@/lib/auth-utils";
import { SearchSourceConnector } from "@/contracts/types/connector.types";
export default function GoogleGmailConnectorPage() { export default function GoogleGmailConnectorPage() {
const router = useRouter(); const router = useRouter();
@ -33,11 +32,12 @@ export default function GoogleGmailConnectorPage() {
const [isConnecting, setIsConnecting] = useState(false); const [isConnecting, setIsConnecting] = useState(false);
const [doesConnectorExist, setDoesConnectorExist] = useState(false); const [doesConnectorExist, setDoesConnectorExist] = useState(false);
const { fetchConnectors } = useSearchSourceConnectors(true, parseInt(searchSpaceId)); const { refetch : fetchConnectors } = useAtomValue(connectorsAtom);
useEffect(() => { useEffect(() => {
fetchConnectors(parseInt(searchSpaceId)).then((data) => { fetchConnectors().then((data) => {
const connector = data.find( const connectors = data.data || [];
const connector = connectors.find(
(c: SearchSourceConnector) => c.connector_type === EnumConnectorName.GOOGLE_GMAIL_CONNECTOR (c: SearchSourceConnector) => c.connector_type === EnumConnectorName.GOOGLE_GMAIL_CONNECTOR
); );
if (connector) { if (connector) {

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { import {
Accordion, Accordion,
AccordionContent, AccordionContent,
@ -37,7 +39,6 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const jiraConnectorFormSchema = z.object({ const jiraConnectorFormSchema = z.object({
@ -73,7 +74,7 @@ export default function JiraConnectorPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<JiraConnectorFormValues>({ const form = useForm<JiraConnectorFormValues>({
@ -90,8 +91,8 @@ export default function JiraConnectorPage() {
const onSubmit = async (values: JiraConnectorFormValues) => { const onSubmit = async (values: JiraConnectorFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.JIRA_CONNECTOR, connector_type: EnumConnectorName.JIRA_CONNECTOR,
config: { config: {
@ -105,8 +106,10 @@ export default function JiraConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Jira connector created successfully!"); toast.success("Jira connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { import {
Accordion, Accordion,
AccordionContent, AccordionContent,
@ -37,7 +39,6 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const linearConnectorFormSchema = z.object({ const linearConnectorFormSchema = z.object({
@ -62,7 +63,7 @@ export default function LinearConnectorPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<LinearConnectorFormValues>({ const form = useForm<LinearConnectorFormValues>({
@ -77,8 +78,8 @@ export default function LinearConnectorPage() {
const onSubmit = async (values: LinearConnectorFormValues) => { const onSubmit = async (values: LinearConnectorFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.LINEAR_CONNECTOR, connector_type: EnumConnectorName.LINEAR_CONNECTOR,
config: { config: {
@ -90,8 +91,10 @@ export default function LinearConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Linear connector created successfully!"); toast.success("Linear connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
@ -30,7 +32,6 @@ import {
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const linkupApiFormSchema = z.object({ const linkupApiFormSchema = z.object({
@ -50,7 +51,7 @@ export default function LinkupApiPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<LinkupApiFormValues>({ const form = useForm<LinkupApiFormValues>({
@ -65,8 +66,8 @@ export default function LinkupApiPage() {
const onSubmit = async (values: LinkupApiFormValues) => { const onSubmit = async (values: LinkupApiFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.LINKUP_API, connector_type: EnumConnectorName.LINKUP_API,
config: { config: {
@ -78,8 +79,10 @@ export default function LinkupApiPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Linkup API connector created successfully!"); toast.success("Linkup API connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Key, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Key, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import Link from "next/link"; import Link from "next/link";
@ -9,6 +10,8 @@ import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
@ -30,10 +33,7 @@ import {
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { import { SearchSourceConnector } from "@/contracts/types/connector.types";
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const lumaConnectorFormSchema = z.object({ const lumaConnectorFormSchema = z.object({
@ -55,10 +55,8 @@ export default function LumaConnectorPage() {
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [doesConnectorExist, setDoesConnectorExist] = useState(false); const [doesConnectorExist, setDoesConnectorExist] = useState(false);
const { fetchConnectors, createConnector } = useSearchSourceConnectors( const { data: connectors } = useAtomValue(connectorsAtom);
true, const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
parseInt(searchSpaceId)
);
// Initialize the form // Initialize the form
const form = useForm<LumaConnectorFormValues>({ const form = useForm<LumaConnectorFormValues>({
@ -69,29 +67,26 @@ export default function LumaConnectorPage() {
}, },
}); });
const { refetch : fetchConnectors } = useAtomValue(connectorsAtom);
useEffect(() => { useEffect(() => {
fetchConnectors(parseInt(searchSpaceId)) fetchConnectors().then((data) => {
.then((data) => { const connectors = data.data || [];
if (data && Array.isArray(data)) { const connector = connectors.find(
const connector = data.find( (c: SearchSourceConnector) => c.connector_type === EnumConnectorName.LUMA_CONNECTOR
(c: SearchSourceConnector) => c.connector_type === EnumConnectorName.LUMA_CONNECTOR );
); if (connector) {
if (connector) { setDoesConnectorExist(true);
setDoesConnectorExist(true); }
} });
} }, []);
})
.catch((error) => {
console.error("Error fetching connectors:", error);
});
}, [fetchConnectors, searchSpaceId]);
// Handle form submission // Handle form submission
const onSubmit = async (values: LumaConnectorFormValues) => { const onSubmit = async (values: LumaConnectorFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.LUMA_CONNECTOR, connector_type: EnumConnectorName.LUMA_CONNECTOR,
config: { config: {
@ -103,8 +98,10 @@ export default function LumaConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Luma connector created successfully!"); toast.success("Luma connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { import {
Accordion, Accordion,
AccordionContent, AccordionContent,
@ -37,7 +39,6 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const notionConnectorFormSchema = z.object({ const notionConnectorFormSchema = z.object({
@ -57,7 +58,7 @@ export default function NotionConnectorPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<NotionConnectorFormValues>({ const form = useForm<NotionConnectorFormValues>({
@ -72,8 +73,8 @@ export default function NotionConnectorPage() {
const onSubmit = async (values: NotionConnectorFormValues) => { const onSubmit = async (values: NotionConnectorFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.NOTION_CONNECTOR, connector_type: EnumConnectorName.NOTION_CONNECTOR,
config: { config: {
@ -85,8 +86,10 @@ export default function NotionConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Notion connector created successfully!"); toast.success("Notion connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
@ -31,7 +33,6 @@ import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
const searxngFormSchema = z.object({ const searxngFormSchema = z.object({
name: z.string().min(3, { name: z.string().min(3, {
@ -67,7 +68,7 @@ export default function SearxngConnectorPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
const form = useForm<SearxngFormValues>({ const form = useForm<SearxngFormValues>({
resolver: zodResolver(searxngFormSchema), resolver: zodResolver(searxngFormSchema),
@ -115,8 +116,8 @@ export default function SearxngConnectorPage() {
config.SEARXNG_VERIFY_SSL = false; config.SEARXNG_VERIFY_SSL = false;
} }
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.SEARXNG_API, connector_type: EnumConnectorName.SEARXNG_API,
config, config,
@ -126,8 +127,10 @@ export default function SearxngConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("SearxNG connector created successfully!"); toast.success("SearxNG connector created successfully!");
router.push(`/dashboard/${searchSpaceId}/connectors`); router.push(`/dashboard/${searchSpaceId}/connectors`);

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { import {
Accordion, Accordion,
AccordionContent, AccordionContent,
@ -37,7 +39,6 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const slackConnectorFormSchema = z.object({ const slackConnectorFormSchema = z.object({
@ -57,7 +58,7 @@ export default function SlackConnectorPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<SlackConnectorFormValues>({ const form = useForm<SlackConnectorFormValues>({
@ -72,8 +73,8 @@ export default function SlackConnectorPage() {
const onSubmit = async (values: SlackConnectorFormValues) => { const onSubmit = async (values: SlackConnectorFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.SLACK_CONNECTOR, connector_type: EnumConnectorName.SLACK_CONNECTOR,
config: { config: {
@ -85,8 +86,10 @@ export default function SlackConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Slack connector created successfully!"); toast.success("Slack connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -8,6 +9,7 @@ import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
@ -30,7 +32,6 @@ import {
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const tavilyApiFormSchema = z.object({ const tavilyApiFormSchema = z.object({
@ -50,7 +51,7 @@ export default function TavilyApiPage() {
const params = useParams(); const params = useParams();
const searchSpaceId = params.search_space_id as string; const searchSpaceId = params.search_space_id as string;
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const { createConnector } = useSearchSourceConnectors(); const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
// Initialize the form // Initialize the form
const form = useForm<TavilyApiFormValues>({ const form = useForm<TavilyApiFormValues>({
@ -65,8 +66,8 @@ export default function TavilyApiPage() {
const onSubmit = async (values: TavilyApiFormValues) => { const onSubmit = async (values: TavilyApiFormValues) => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.TAVILY_API, connector_type: EnumConnectorName.TAVILY_API,
config: { config: {
@ -78,8 +79,10 @@ export default function TavilyApiPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Tavily API connector created successfully!"); toast.success("Tavily API connector created successfully!");

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { ArrowLeft, Check, Globe, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Globe, Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import Link from "next/link"; import Link from "next/link";
@ -9,6 +10,8 @@ import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod"; import * as z from "zod";
import { createConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
@ -31,10 +34,7 @@ import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { import { SearchSourceConnector } from "@/contracts/types/connector.types";
type SearchSourceConnector,
useSearchSourceConnectors,
} from "@/hooks/use-search-source-connectors";
// Define the form schema with Zod // Define the form schema with Zod
const webcrawlerConnectorFormSchema = z.object({ const webcrawlerConnectorFormSchema = z.object({
@ -55,10 +55,8 @@ export default function WebcrawlerConnectorPage() {
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [doesConnectorExist, setDoesConnectorExist] = useState(false); const [doesConnectorExist, setDoesConnectorExist] = useState(false);
const { fetchConnectors, createConnector } = useSearchSourceConnectors( const { refetch : fetchConnectors } = useAtomValue(connectorsAtom);
true, const { mutateAsync: createConnector } = useAtomValue(createConnectorMutationAtom);
parseInt(searchSpaceId)
);
// Initialize the form // Initialize the form
const form = useForm<WebcrawlerConnectorFormValues>({ const form = useForm<WebcrawlerConnectorFormValues>({
@ -71,22 +69,16 @@ export default function WebcrawlerConnectorPage() {
}); });
useEffect(() => { useEffect(() => {
fetchConnectors(parseInt(searchSpaceId)) fetchConnectors().then((data) => {
.then((data) => { const connectors = data.data || [];
if (data && Array.isArray(data)) { const connector = connectors.find(
const connector = data.find( (c: SearchSourceConnector) => c.connector_type === EnumConnectorName.WEBCRAWLER_CONNECTOR
(c: SearchSourceConnector) => );
c.connector_type === EnumConnectorName.WEBCRAWLER_CONNECTOR if (connector) {
); setDoesConnectorExist(true);
if (connector) { }
setDoesConnectorExist(true); });
} }, []);
}
})
.catch((error) => {
console.error("Error fetching connectors:", error);
});
}, [fetchConnectors, searchSpaceId]);
// Handle form submission // Handle form submission
const onSubmit = async (values: WebcrawlerConnectorFormValues) => { const onSubmit = async (values: WebcrawlerConnectorFormValues) => {
@ -104,8 +96,8 @@ export default function WebcrawlerConnectorPage() {
config.INITIAL_URLS = values.initial_urls; config.INITIAL_URLS = values.initial_urls;
} }
await createConnector( await createConnector({
{ data: {
name: values.name, name: values.name,
connector_type: EnumConnectorName.WEBCRAWLER_CONNECTOR, connector_type: EnumConnectorName.WEBCRAWLER_CONNECTOR,
config: config, config: config,
@ -115,8 +107,10 @@ export default function WebcrawlerConnectorPage() {
indexing_frequency_minutes: null, indexing_frequency_minutes: null,
next_scheduled_at: null, next_scheduled_at: null,
}, },
parseInt(searchSpaceId) queryParams: {
); search_space_id: searchSpaceId,
},
});
toast.success("Webcrawler connector created successfully!"); toast.success("Webcrawler connector created successfully!");

View file

@ -0,0 +1,100 @@
import { atomWithMutation } from "jotai-tanstack-query";
import { toast } from "sonner";
import type {
CreateConnectorRequest,
DeleteConnectorRequest,
GetConnectorsResponse,
IndexConnectorRequest,
IndexConnectorResponse,
UpdateConnectorRequest,
} from "@/contracts/types/connector.types";
import { connectorsApiService } from "@/lib/apis/connectors-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys";
import { queryClient } from "@/lib/query-client/client";
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
export const createConnectorMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
return {
mutationKey: cacheKeys.connectors.all(searchSpaceId!),
enabled: !!searchSpaceId,
mutationFn: async (request: CreateConnectorRequest) => {
return connectorsApiService.createConnector(request);
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: cacheKeys.connectors.all(searchSpaceId!),
});
},
};
});
export const updateConnectorMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
return {
mutationKey: cacheKeys.connectors.all(searchSpaceId!),
enabled: !!searchSpaceId,
mutationFn: async (request: UpdateConnectorRequest) => {
return connectorsApiService.updateConnector(request);
},
onSuccess: (_, request: UpdateConnectorRequest) => {
queryClient.invalidateQueries({
queryKey: cacheKeys.connectors.all(searchSpaceId!),
});
queryClient.invalidateQueries({
queryKey: cacheKeys.connectors.byId(String(request.id)),
});
},
};
});
export const deleteConnectorMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
return {
mutationKey: cacheKeys.connectors.all(searchSpaceId!),
enabled: !!searchSpaceId,
mutationFn: async (request: DeleteConnectorRequest) => {
return connectorsApiService.deleteConnector(request);
},
onSuccess: (_, request: DeleteConnectorRequest) => {
queryClient.setQueryData(
cacheKeys.connectors.all(searchSpaceId!),
(oldData: GetConnectorsResponse | undefined) => {
if (!oldData) return oldData;
return oldData.filter((connector) => connector.id !== request.id);
}
);
queryClient.invalidateQueries({
queryKey: cacheKeys.connectors.byId(String(request.id)),
});
},
};
});
export const indexConnectorMutationAtom = atomWithMutation((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
return {
mutationKey: cacheKeys.connectors.index(),
enabled: !!searchSpaceId,
mutationFn: async (request: IndexConnectorRequest) => {
return connectorsApiService.indexConnector(request);
},
onSuccess: (response: IndexConnectorResponse) => {
toast.success(response.message);
queryClient.invalidateQueries({
queryKey: cacheKeys.connectors.all(searchSpaceId!),
});
queryClient.invalidateQueries({
queryKey: cacheKeys.connectors.byId(String(response.connector_id)),
});
},
};
});

View file

@ -0,0 +1,21 @@
import { atomWithQuery } from "jotai-tanstack-query";
import { connectorsApiService } from "@/lib/apis/connectors-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys";
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
export const connectorsAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
return {
queryKey: cacheKeys.connectors.all(searchSpaceId!),
enabled: !!searchSpaceId,
staleTime: 5 * 60 * 1000, // 5 minutes
queryFn: async () => {
return connectorsApiService.getConnectors({
queryParams: {
search_space_id: searchSpaceId!,
},
});
},
};
});

View file

@ -0,0 +1,7 @@
import { atom } from "jotai";
import type { GetConnectorsRequest } from "@/contracts/types/connector.types";
export const globalConnectorsQueryParamsAtom = atom<GetConnectorsRequest["queryParams"]>({
skip: 0,
limit: 10,
});

View file

@ -0,0 +1,159 @@
import { z } from "zod";
import { paginationQueryParams } from ".";
export const searchSourceConnectorTypeEnum = z.enum([
"SERPER_API",
"TAVILY_API",
"SEARXNG_API",
"LINKUP_API",
"BAIDU_SEARCH_API",
"SLACK_CONNECTOR",
"NOTION_CONNECTOR",
"GITHUB_CONNECTOR",
"LINEAR_CONNECTOR",
"DISCORD_CONNECTOR",
"JIRA_CONNECTOR",
"CONFLUENCE_CONNECTOR",
"CLICKUP_CONNECTOR",
"GOOGLE_CALENDAR_CONNECTOR",
"GOOGLE_GMAIL_CONNECTOR",
"AIRTABLE_CONNECTOR",
"LUMA_CONNECTOR",
"ELASTICSEARCH_CONNECTOR",
"WEBCRAWLER_CONNECTOR",
"BOOKSTACK_CONNECTOR",
]);
export const searchSourceConnector = z.object({
id: z.number(),
name: z.string(),
connector_type: searchSourceConnectorTypeEnum,
is_indexable: z.boolean(),
last_indexed_at: z.string().nullable(),
config: z.record(z.string(), z.any()),
periodic_indexing_enabled: z.boolean(),
indexing_frequency_minutes: z.number().nullable(),
next_scheduled_at: z.string().nullable(),
search_space_id: z.number(),
user_id: z.string(),
created_at: z.string(),
});
/**
* Get connectors
*/
export const getConnectorsRequest = z.object({
queryParams: paginationQueryParams
.pick({ skip: true, limit: true })
.extend({
search_space_id: z.number().or(z.string()).nullish(),
})
.nullish(),
});
export const getConnectorsResponse = z.array(searchSourceConnector);
/**
* Get connector
*/
export const getConnectorRequest = searchSourceConnector.pick({ id: true });
export const getConnectorResponse = searchSourceConnector;
/**
* Create connector
*/
export const createConnectorRequest = z.object({
data: searchSourceConnector.pick({
name: true,
connector_type: true,
is_indexable: true,
last_indexed_at: true,
config: true,
periodic_indexing_enabled: true,
indexing_frequency_minutes: true,
next_scheduled_at: true,
}),
queryParams: z.object({
search_space_id: z.number().or(z.string()),
}),
});
export const createConnectorResponse = searchSourceConnector;
/**
* Update connector
*/
export const updateConnectorRequest = z.object({
id: z.number(),
data: searchSourceConnector
.pick({
name: true,
connector_type: true,
is_indexable: true,
last_indexed_at: true,
config: true,
periodic_indexing_enabled: true,
indexing_frequency_minutes: true,
next_scheduled_at: true,
})
.partial(),
});
export const updateConnectorResponse = searchSourceConnector;
/**
* Delete connector
*/
export const deleteConnectorRequest = searchSourceConnector.pick({ id: true });
export const deleteConnectorResponse = z.object({
message: z.literal("Search source connector deleted successfully"),
});
/**
* Index connector
*/
export const indexConnectorRequest = z.object({
connector_id: z.number(),
queryParams: z.object({
search_space_id: z.number().or(z.string()),
start_date: z.string().optional(),
end_date: z.string().optional(),
}),
});
export const indexConnectorResponse = z.object({
message: z.string(),
connector_id: z.number(),
search_space_id: z.number(),
indexing_from: z.string(),
indexing_to: z.string(),
});
/**
* List GitHub repositories
*/
export const listGitHubRepositoriesRequest = z.object({
github_pat: z.string(),
});
export const listGitHubRepositoriesResponse = z.array(z.record(z.string(), z.any()));
// Inferred types
export type SearchSourceConnectorType = z.infer<typeof searchSourceConnectorTypeEnum>;
export type SearchSourceConnector = z.infer<typeof searchSourceConnector>;
export type GetConnectorsRequest = z.infer<typeof getConnectorsRequest>;
export type GetConnectorsResponse = z.infer<typeof getConnectorsResponse>;
export type GetConnectorRequest = z.infer<typeof getConnectorRequest>;
export type GetConnectorResponse = z.infer<typeof getConnectorResponse>;
export type CreateConnectorRequest = z.infer<typeof createConnectorRequest>;
export type CreateConnectorResponse = z.infer<typeof createConnectorResponse>;
export type UpdateConnectorRequest = z.infer<typeof updateConnectorRequest>;
export type UpdateConnectorResponse = z.infer<typeof updateConnectorResponse>;
export type DeleteConnectorRequest = z.infer<typeof deleteConnectorRequest>;
export type DeleteConnectorResponse = z.infer<typeof deleteConnectorResponse>;
export type IndexConnectorRequest = z.infer<typeof indexConnectorRequest>;
export type IndexConnectorResponse = z.infer<typeof indexConnectorResponse>;
export type ListGitHubRepositoriesRequest = z.infer<typeof listGitHubRepositoriesRequest>;
export type ListGitHubRepositoriesResponse = z.infer<typeof listGitHubRepositoriesResponse>;

View file

@ -1,8 +1,11 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { updateConnectorMutationAtom } from "@/atoms/connectors/connector-mutation.atoms";
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
import { import {
type EditConnectorFormValues, type EditConnectorFormValues,
type EditMode, type EditMode,
@ -11,10 +14,8 @@ import {
type GithubRepo, type GithubRepo,
githubPatSchema, githubPatSchema,
} from "@/components/editConnector/types"; } from "@/components/editConnector/types";
import { import type { EnumConnectorName } from "@/contracts/enums/connector";
type SearchSourceConnector, import type { SearchSourceConnector } from "@/hooks/use-search-source-connectors";
useSearchSourceConnectors,
} from "@/hooks/use-search-source-connectors";
import { authenticatedFetch } from "@/lib/auth-utils"; import { authenticatedFetch } from "@/lib/auth-utils";
const normalizeListInput = (value: unknown): string[] => { const normalizeListInput = (value: unknown): string[] => {
@ -51,11 +52,8 @@ const normalizeBoolean = (value: unknown): boolean | null => {
export function useConnectorEditPage(connectorId: number, searchSpaceId: string) { export function useConnectorEditPage(connectorId: number, searchSpaceId: string) {
const router = useRouter(); const router = useRouter();
const { const { data: connectors = [], isLoading: connectorsLoading } = useAtomValue(connectorsAtom);
connectors, const { mutateAsync: updateConnector } = useAtomValue(updateConnectorMutationAtom);
updateConnector,
isLoading: connectorsLoading,
} = useSearchSourceConnectors(false, parseInt(searchSpaceId));
// State managed by the hook // State managed by the hook
const [connector, setConnector] = useState<SearchSourceConnector | null>(null); const [connector, setConnector] = useState<SearchSourceConnector | null>(null);
@ -532,7 +530,13 @@ export function useConnectorEditPage(connectorId: number, searchSpaceId: string)
} }
try { try {
await updateConnector(connectorId, updatePayload); await updateConnector({
id: connectorId,
data: {
...updatePayload,
connector_type: connector.connector_type as EnumConnectorName,
},
});
toast.success("Connector updated!"); toast.success("Connector updated!");
const newlySavedConfig = updatePayload.config || originalConfig; const newlySavedConfig = updatePayload.config || originalConfig;
setOriginalConfig(newlySavedConfig); setOriginalConfig(newlySavedConfig);

View file

@ -1,114 +0,0 @@
import { authenticatedFetch } from "@/lib/auth-utils";
// Types for connector API
export interface ConnectorConfig {
[key: string]: string;
}
export interface Connector {
id: number;
name: string;
connector_type: string;
config: ConnectorConfig;
created_at: string;
user_id: string;
}
export interface CreateConnectorRequest {
name: string;
connector_type: string;
config: ConnectorConfig;
}
// Get connector type display name
export const getConnectorTypeDisplay = (type: string): string => {
const typeMap: Record<string, string> = {
TAVILY_API: "Tavily API",
SEARXNG_API: "SearxNG",
};
return typeMap[type] || type;
};
// API service for connectors
export const ConnectorService = {
// Create a new connector
async createConnector(data: CreateConnectorRequest): Promise<Connector> {
const response = await authenticatedFetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-source-connectors`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
}
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || "Failed to create connector");
}
return response.json();
},
// Get all connectors
async getConnectors(skip = 0, limit = 100): Promise<Connector[]> {
const response = await authenticatedFetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-source-connectors?skip=${skip}&limit=${limit}`,
{ method: "GET" }
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || "Failed to fetch connectors");
}
return response.json();
},
// Get a specific connector
async getConnector(connectorId: number): Promise<Connector> {
const response = await authenticatedFetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-source-connectors/${connectorId}`,
{ method: "GET" }
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || "Failed to fetch connector");
}
return response.json();
},
// Update a connector
async updateConnector(connectorId: number, data: CreateConnectorRequest): Promise<Connector> {
const response = await authenticatedFetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-source-connectors/${connectorId}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
}
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || "Failed to update connector");
}
return response.json();
},
// Delete a connector
async deleteConnector(connectorId: number): Promise<void> {
const response = await authenticatedFetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-source-connectors/${connectorId}`,
{ method: "DELETE" }
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || "Failed to delete connector");
}
},
};

View file

@ -0,0 +1,200 @@
import {
type CreateConnectorRequest,
createConnectorRequest,
createConnectorResponse,
type DeleteConnectorRequest,
deleteConnectorRequest,
deleteConnectorResponse,
type GetConnectorRequest,
type GetConnectorsRequest,
getConnectorRequest,
getConnectorResponse,
getConnectorsRequest,
getConnectorsResponse,
type IndexConnectorRequest,
indexConnectorRequest,
indexConnectorResponse,
type ListGitHubRepositoriesRequest,
listGitHubRepositoriesRequest,
listGitHubRepositoriesResponse,
type UpdateConnectorRequest,
updateConnectorRequest,
updateConnectorResponse,
} from "@/contracts/types/connector.types";
import { ValidationError } from "../error";
import { baseApiService } from "./base-api.service";
class ConnectorsApiService {
/**
* Get all connectors for a search space
*/
getConnectors = async (request: GetConnectorsRequest) => {
const parsedRequest = getConnectorsRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
// Transform query params to be string values
const transformedQueryParams = parsedRequest.data.queryParams
? Object.fromEntries(
Object.entries(parsedRequest.data.queryParams).map(([k, v]) => {
return [k, String(v)];
})
)
: undefined;
const queryParams = transformedQueryParams
? new URLSearchParams(transformedQueryParams).toString()
: "";
return baseApiService.get(
`/api/v1/search-source-connectors?${queryParams}`,
getConnectorsResponse
);
};
/**
* Get a single connector by ID
*/
getConnector = async (request: GetConnectorRequest) => {
const parsedRequest = getConnectorRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
return baseApiService.get(
`/api/v1/search-source-connectors/${request.id}`,
getConnectorResponse
);
};
/**
* Create a new connector
*/
createConnector = async (request: CreateConnectorRequest) => {
const parsedRequest = createConnectorRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
const { data, queryParams } = parsedRequest.data;
// Transform query params to be string values
const transformedQueryParams = Object.fromEntries(
Object.entries(queryParams).map(([k, v]) => {
return [k, String(v)];
})
);
const queryString = new URLSearchParams(transformedQueryParams).toString();
return baseApiService.post(
`/api/v1/search-source-connectors?${queryString}`,
createConnectorResponse,
{
body: data,
}
);
};
/**
* Update an existing connector
*/
updateConnector = async (request: UpdateConnectorRequest) => {
const parsedRequest = updateConnectorRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
const { id, data } = parsedRequest.data;
return baseApiService.put(`/api/v1/search-source-connectors/${id}`, updateConnectorResponse, {
body: data,
});
};
/**
* Delete a connector
*/
deleteConnector = async (request: DeleteConnectorRequest) => {
const parsedRequest = deleteConnectorRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
return baseApiService.delete(
`/api/v1/search-source-connectors/${request.id}`,
deleteConnectorResponse
);
};
/**
* Index connector content
*/
indexConnector = async (request: IndexConnectorRequest) => {
const parsedRequest = indexConnectorRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
const { connector_id, queryParams } = parsedRequest.data;
// Transform query params to be string values
const transformedQueryParams = Object.fromEntries(
Object.entries(queryParams).map(([k, v]) => {
return [k, String(v)];
})
);
const queryString = new URLSearchParams(transformedQueryParams).toString();
return baseApiService.post(
`/api/v1/search-source-connectors/${connector_id}/index?${queryString}`,
indexConnectorResponse
);
};
/**
* List GitHub repositories using a Personal Access Token
*/
listGitHubRepositories = async (request: ListGitHubRepositoriesRequest) => {
const parsedRequest = listGitHubRepositoriesRequest.safeParse(request);
if (!parsedRequest.success) {
console.error("Invalid request:", parsedRequest.error);
const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", ");
throw new ValidationError(`Invalid request: ${errorMessage}`);
}
return baseApiService.post(`/api/v1/github/repositories`, listGitHubRepositoriesResponse, {
body: parsedRequest.data,
});
};
}
export const connectorsApiService = new ConnectorsApiService();

View file

@ -1,3 +1,4 @@
import type { GetConnectorsRequest } from "@/contracts/types/connector.types";
import type { GetDocumentsRequest } from "@/contracts/types/document.types"; import type { GetDocumentsRequest } from "@/contracts/types/document.types";
import type { GetLogsRequest } from "@/contracts/types/log.types"; import type { GetLogsRequest } from "@/contracts/types/log.types";
import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.types"; import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.types";
@ -60,4 +61,11 @@ export const cacheKeys = {
all: (searchSpaceId: string) => ["invites", searchSpaceId] as const, all: (searchSpaceId: string) => ["invites", searchSpaceId] as const,
info: (inviteCode: string) => ["invites", "info", inviteCode] as const, info: (inviteCode: string) => ["invites", "info", inviteCode] as const,
}, },
connectors: {
all: (searchSpaceId: string) => ["connectors", searchSpaceId] as const,
withQueryParams: (queries: GetConnectorsRequest["queryParams"]) =>
["connectors", ...(queries ? Object.values(queries) : [])] as const,
byId: (connectorId: string) => ["connector", connectorId] as const,
index: () => ["connector", "index"] as const,
},
}; };