From 9a98742f8139b1ec864809a5c0f1d0eb28ad2f00 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 30 Jul 2025 21:37:12 +0200 Subject: [PATCH] Add web implementation for clickup connector --- .../15_add_clickup_connector_enums.py | 8 +- .../app/services/connector_service.py | 8 +- .../connectors/[connector_id]/edit/page.tsx | 11 + .../connectors/add/clickup-connector/page.tsx | 232 ++++++++++++++++++ .../[search_space_id]/connectors/add/page.tsx | 8 + .../documents/(manage)/page.tsx | 2 + 6 files changed, 263 insertions(+), 6 deletions(-) create mode 100644 surfsense_web/app/dashboard/[search_space_id]/connectors/add/clickup-connector/page.tsx diff --git a/surfsense_backend/alembic/versions/15_add_clickup_connector_enums.py b/surfsense_backend/alembic/versions/15_add_clickup_connector_enums.py index 4f339c0d7..8882cfccc 100644 --- a/surfsense_backend/alembic/versions/15_add_clickup_connector_enums.py +++ b/surfsense_backend/alembic/versions/15_add_clickup_connector_enums.py @@ -6,15 +6,15 @@ Create Date: 2025-07-29 12:00:00.000000 """ -from typing import Sequence, Union +from collections.abc import Sequence from alembic import op # revision identifiers, used by Alembic. revision: str = "15_add_clickup_connector_enums" -down_revision: Union[str, None] = "14_add_confluence_connector_enums" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None +down_revision: str | None = "14_add_confluence_connector_enums" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None def upgrade() -> None: diff --git a/surfsense_backend/app/services/connector_service.py b/surfsense_backend/app/services/connector_service.py index 74f7c5a70..de9c4c22a 100644 --- a/surfsense_backend/app/services/connector_service.py +++ b/surfsense_backend/app/services/connector_service.py @@ -1246,7 +1246,9 @@ class ConnectorService: if task_priority: description_parts.append(f"Priority: {task_priority}") if task_assignees: - assignee_names = [assignee.get("username", "Unknown") for assignee in task_assignees] + assignee_names = [ + assignee.get("username", "Unknown") for assignee in task_assignees + ] description_parts.append(f"Assignees: {', '.join(assignee_names)}") if task_due_date: description_parts.append(f"Due: {task_due_date}") @@ -1255,7 +1257,9 @@ class ConnectorService: if task_space_name: description_parts.append(f"Space: {task_space_name}") - description = " | ".join(description_parts) if description_parts else "ClickUp Task" + description = ( + " | ".join(description_parts) if description_parts else "ClickUp Task" + ) source = { "id": document.get("id", self.source_id_counter), diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx index a535319a5..abd223a36 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx @@ -228,6 +228,17 @@ export default function EditConnectorPage() { )} + {/* == ClickUp == */} + {connector.connector_type === "CLICKUP_CONNECTOR" && ( + + )} + {/* == Linkup == */} {connector.connector_type === "LINKUP_API" && ( ; + +export default function ClickUpConnectorPage({ params }: ClickUpConnectorPageProps) { + const router = useRouter(); + const { createConnector } = useSearchSourceConnectors(); + const [isLoading, setIsLoading] = useState(false); + const [showApiToken, setShowApiToken] = useState(false); + + // Initialize the form with react-hook-form and zod validation + const form = useForm({ + resolver: zodResolver(clickupConnectorFormSchema), + defaultValues: { + name: "ClickUp Connector", + api_token: "", + }, + }); + + // Handle form submission + async function onSubmit(values: ClickUpConnectorFormValues) { + setIsLoading(true); + + try { + const connectorData = { + name: values.name, + connector_type: "CLICKUP_CONNECTOR", + is_indexable: true, + config: { + CLICKUP_API_TOKEN: values.api_token, + }, + last_indexed_at: null, + }; + + await createConnector(connectorData); + + toast.success("ClickUp connector created successfully!"); + router.push(`/dashboard/${params.search_space_id}/connectors`); + } catch (error) { + console.error("Error creating ClickUp connector:", error); + toast.error("Failed to create ClickUp connector. Please try again."); + } finally { + setIsLoading(false); + } + } + + return ( +
+
+ + + Back to connectors + +

Add ClickUp Connector

+

+ Connect your ClickUp workspace to search and retrieve tasks. +

+
+ + + + ClickUp Configuration + + Enter your ClickUp API token to connect your workspace. You can generate a personal API token from your ClickUp settings. + + + +
+ + ( + + Connector Name + + + + + A friendly name to identify this ClickUp connector. + + + + )} + /> + + ( + + ClickUp API Token + +
+ + +
+
+ + Your ClickUp personal API token. You can generate one in your{" "} + + ClickUp settings + + + . + + +
+ )} + /> + +
+ + +
+ + +
+
+ + + + How to get your ClickUp API Token + + +
+

+ 1. Log in to your ClickUp account +

+

+ 2. Click your avatar in the upper-right corner and select "Settings" +

+

+ 3. In the sidebar, click "Apps" +

+

+ 4. Under "API Token", click "Generate" or "Regenerate" +

+

+ 5. Copy the generated token and paste it above +

+
+
+ + Go to ClickUp API Settings + + +
+
+
+
+ ); +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx index f6dbd51c9..a1be455f6 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx @@ -15,6 +15,7 @@ import { IconMail, IconTicket, IconWorldWww, + IconChecklist, } from "@tabler/icons-react"; import { AnimatePresence, motion, type Variants } from "framer-motion"; import Link from "next/link"; @@ -107,6 +108,13 @@ const connectorCategories: ConnectorCategory[] = [ icon: , status: "available", }, + { + id: "clickup-connector", + title: "ClickUp", + description: "Connect to ClickUp to search tasks, comments and project data.", + icon: , + status: "available", + }, ], }, { diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx index e9f49c990..727a7698b 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx @@ -7,6 +7,7 @@ import { IconBrandNotion, IconBrandSlack, IconBrandYoutube, + IconChecklist, IconLayoutKanban, IconTicket, } from "@tabler/icons-react"; @@ -146,6 +147,7 @@ const documentTypeIcons = { JIRA_CONNECTOR: IconTicket, DISCORD_CONNECTOR: IconBrandDiscord, CONFLUENCE_CONNECTOR: IconBook, + CLICKUP_CONNECTOR: IconChecklist, } as const; const columns: ColumnDef[] = [