From d37417cbe9aaa18dd130d6de3f0cfed77d1ecdf8 Mon Sep 17 00:00:00 2001 From: Oscar Zhou Date: Tue, 14 Apr 2026 17:39:11 +0930 Subject: [PATCH] refactor: move pure utility functions out of UI components (#1194) --- .../tabs/active-connectors-tab.tsx | 2 +- .../components/assistant-ui/thread.tsx | 2 +- .../comment-item/comment-item.tsx | 5 +-- .../components/documents/DocumentTypeIcon.tsx | 42 +------------------ .../components/documents/DocumentsFilters.tsx | 3 +- .../layout/ui/sidebar/InboxSidebar.tsx | 4 +- .../ui/sidebar/SidebarSlideOutPanel.tsx | 2 +- surfsense_web/lib/comments/utils.ts | 4 ++ .../lib/documents/document-type-labels.ts | 41 ++++++++++++++++++ surfsense_web/lib/layout-events.ts | 1 + 10 files changed, 55 insertions(+), 51 deletions(-) create mode 100644 surfsense_web/lib/comments/utils.ts create mode 100644 surfsense_web/lib/documents/document-type-labels.ts create mode 100644 surfsense_web/lib/layout-events.ts diff --git a/surfsense_web/components/assistant-ui/connector-popup/tabs/active-connectors-tab.tsx b/surfsense_web/components/assistant-ui/connector-popup/tabs/active-connectors-tab.tsx index c08871445..eb161f485 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/tabs/active-connectors-tab.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/tabs/active-connectors-tab.tsx @@ -2,7 +2,7 @@ import { Search, Unplug } from "lucide-react"; import type { FC } from "react"; -import { getDocumentTypeLabel } from "@/components/documents/DocumentTypeIcon"; +import { getDocumentTypeLabel } from "@/lib/documents/document-type-labels"; import { Button } from "@/components/ui/button"; import { Spinner } from "@/components/ui/spinner"; import { TabsContent } from "@/components/ui/tabs"; diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 59797fc72..dcb1e3e9e 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -60,7 +60,7 @@ import { } from "@/components/assistant-ui/inline-mention-editor"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { UserMessage } from "@/components/assistant-ui/user-message"; -import { SLIDEOUT_PANEL_OPENED_EVENT } from "@/components/layout/ui/sidebar/SidebarSlideOutPanel"; +import { SLIDEOUT_PANEL_OPENED_EVENT } from "@/lib/layout-events"; import { DocumentMentionPicker, type DocumentMentionPickerRef, diff --git a/surfsense_web/components/chat-comments/comment-item/comment-item.tsx b/surfsense_web/components/chat-comments/comment-item/comment-item.tsx index eb374ba49..359439d07 100644 --- a/surfsense_web/components/chat-comments/comment-item/comment-item.tsx +++ b/surfsense_web/components/chat-comments/comment-item/comment-item.tsx @@ -9,6 +9,7 @@ import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import { CommentComposer } from "../comment-composer/comment-composer"; import { CommentActions } from "./comment-actions"; +import { convertRenderedToDisplay } from "@/lib/comments/utils"; import type { CommentItemProps } from "./types"; function getInitials(name: string | null, email: string): string { @@ -69,10 +70,6 @@ function formatTimestamp(dateString: string): string { ); } -export function convertRenderedToDisplay(contentRendered: string): string { - // Convert @{DisplayName} format to @DisplayName for editing - return contentRendered.replace(/@\{([^}]+)\}/g, "@$1"); -} function renderMentions(content: string): React.ReactNode { // Match @{DisplayName} format from backend diff --git a/surfsense_web/components/documents/DocumentTypeIcon.tsx b/surfsense_web/components/documents/DocumentTypeIcon.tsx index 5c03d96fa..4c6507081 100644 --- a/surfsense_web/components/documents/DocumentTypeIcon.tsx +++ b/surfsense_web/components/documents/DocumentTypeIcon.tsx @@ -4,52 +4,12 @@ import type React from "react"; import { useEffect, useRef, useState } from "react"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; +import { getDocumentTypeLabel } from "@/lib/documents/document-type-labels"; export function getDocumentTypeIcon(type: string, className?: string): React.ReactNode { return getConnectorIcon(type, className); } -export function getDocumentTypeLabel(type: string): string { - const labelMap: Record = { - EXTENSION: "Extension", - CRAWLED_URL: "Web Page", - FILE: "File", - SLACK_CONNECTOR: "Slack", - TEAMS_CONNECTOR: "Microsoft Teams", - ONEDRIVE_FILE: "OneDrive", - DROPBOX_FILE: "Dropbox", - NOTION_CONNECTOR: "Notion", - YOUTUBE_VIDEO: "YouTube Video", - GITHUB_CONNECTOR: "GitHub", - LINEAR_CONNECTOR: "Linear", - DISCORD_CONNECTOR: "Discord", - JIRA_CONNECTOR: "Jira", - CONFLUENCE_CONNECTOR: "Confluence", - CLICKUP_CONNECTOR: "ClickUp", - GOOGLE_CALENDAR_CONNECTOR: "Google Calendar", - GOOGLE_GMAIL_CONNECTOR: "Gmail", - GOOGLE_DRIVE_FILE: "Google Drive", - AIRTABLE_CONNECTOR: "Airtable", - LUMA_CONNECTOR: "Luma", - ELASTICSEARCH_CONNECTOR: "Elasticsearch", - BOOKSTACK_CONNECTOR: "BookStack", - CIRCLEBACK: "Circleback", - OBSIDIAN_CONNECTOR: "Obsidian", - LOCAL_FOLDER_FILE: "Local Folder", - SURFSENSE_DOCS: "SurfSense Docs", - NOTE: "Note", - COMPOSIO_GOOGLE_DRIVE_CONNECTOR: "Composio Google Drive", - COMPOSIO_GMAIL_CONNECTOR: "Composio Gmail", - COMPOSIO_GOOGLE_CALENDAR_CONNECTOR: "Composio Google Calendar", - }; - return ( - labelMap[type] || - type - .split("_") - .map((word) => word.charAt(0) + word.slice(1).toLowerCase()) - .join(" ") - ); -} export function DocumentTypeChip({ type, className }: { type: string; className?: string }) { const icon = getDocumentTypeIcon(type, "h-4 w-4"); diff --git a/surfsense_web/components/documents/DocumentsFilters.tsx b/surfsense_web/components/documents/DocumentsFilters.tsx index d43f3680b..4e61e0da9 100644 --- a/surfsense_web/components/documents/DocumentsFilters.tsx +++ b/surfsense_web/components/documents/DocumentsFilters.tsx @@ -11,7 +11,8 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import type { DocumentTypeEnum } from "@/contracts/types/document.types"; -import { getDocumentTypeIcon, getDocumentTypeLabel } from "./DocumentTypeIcon"; +import { getDocumentTypeIcon } from "./DocumentTypeIcon"; +import { getDocumentTypeLabel } from "@/lib/documents/document-type-labels"; export function DocumentsFilters({ typeCounts: typeCountsRecord, diff --git a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx index 371f3dc6d..5d8b530c0 100644 --- a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx @@ -22,8 +22,8 @@ import { useParams, useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from "react"; import { setTargetCommentIdAtom } from "@/atoms/chat/current-thread.atom"; -import { convertRenderedToDisplay } from "@/components/chat-comments/comment-item/comment-item"; -import { getDocumentTypeLabel } from "@/components/documents/DocumentTypeIcon"; +import { convertRenderedToDisplay } from "@/lib/comments/utils"; +import { getDocumentTypeLabel } from "@/lib/documents/document-type-labels"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/animated-tabs"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarSlideOutPanel.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarSlideOutPanel.tsx index 5195082cd..661f76ed3 100644 --- a/surfsense_web/components/layout/ui/sidebar/SidebarSlideOutPanel.tsx +++ b/surfsense_web/components/layout/ui/sidebar/SidebarSlideOutPanel.tsx @@ -3,8 +3,8 @@ import { AnimatePresence, motion } from "motion/react"; import { useCallback, useEffect } from "react"; import { useMediaQuery } from "@/hooks/use-media-query"; +import { SLIDEOUT_PANEL_OPENED_EVENT } from "@/lib/layout-events"; -export const SLIDEOUT_PANEL_OPENED_EVENT = "slideout-panel-opened"; interface SidebarSlideOutPanelProps { open: boolean; diff --git a/surfsense_web/lib/comments/utils.ts b/surfsense_web/lib/comments/utils.ts new file mode 100644 index 000000000..acbcee506 --- /dev/null +++ b/surfsense_web/lib/comments/utils.ts @@ -0,0 +1,4 @@ +export function convertRenderedToDisplay(contentRendered: string): string { + // Convert @{DisplayName} format to @DisplayName for editing + return contentRendered.replace(/@\{([^}]+)\}/g, "@$1"); +} diff --git a/surfsense_web/lib/documents/document-type-labels.ts b/surfsense_web/lib/documents/document-type-labels.ts new file mode 100644 index 000000000..844961886 --- /dev/null +++ b/surfsense_web/lib/documents/document-type-labels.ts @@ -0,0 +1,41 @@ +export function getDocumentTypeLabel(type: string): string { + const labelMap: Record = { + EXTENSION: "Extension", + CRAWLED_URL: "Web Page", + FILE: "File", + SLACK_CONNECTOR: "Slack", + TEAMS_CONNECTOR: "Microsoft Teams", + ONEDRIVE_FILE: "OneDrive", + DROPBOX_FILE: "Dropbox", + NOTION_CONNECTOR: "Notion", + YOUTUBE_VIDEO: "YouTube Video", + GITHUB_CONNECTOR: "GitHub", + LINEAR_CONNECTOR: "Linear", + DISCORD_CONNECTOR: "Discord", + JIRA_CONNECTOR: "Jira", + CONFLUENCE_CONNECTOR: "Confluence", + CLICKUP_CONNECTOR: "ClickUp", + GOOGLE_CALENDAR_CONNECTOR: "Google Calendar", + GOOGLE_GMAIL_CONNECTOR: "Gmail", + GOOGLE_DRIVE_FILE: "Google Drive", + AIRTABLE_CONNECTOR: "Airtable", + LUMA_CONNECTOR: "Luma", + ELASTICSEARCH_CONNECTOR: "Elasticsearch", + BOOKSTACK_CONNECTOR: "BookStack", + CIRCLEBACK: "Circleback", + OBSIDIAN_CONNECTOR: "Obsidian", + LOCAL_FOLDER_FILE: "Local Folder", + SURFSENSE_DOCS: "SurfSense Docs", + NOTE: "Note", + COMPOSIO_GOOGLE_DRIVE_CONNECTOR: "Composio Google Drive", + COMPOSIO_GMAIL_CONNECTOR: "Composio Gmail", + COMPOSIO_GOOGLE_CALENDAR_CONNECTOR: "Composio Google Calendar", + }; + return ( + labelMap[type] || + type + .split("_") + .map((word) => word.charAt(0) + word.slice(1).toLowerCase()) + .join(" ") + ); +} diff --git a/surfsense_web/lib/layout-events.ts b/surfsense_web/lib/layout-events.ts new file mode 100644 index 000000000..45c52f7a4 --- /dev/null +++ b/surfsense_web/lib/layout-events.ts @@ -0,0 +1 @@ +export const SLIDEOUT_PANEL_OPENED_EVENT = "slideout-panel-opened";