diff --git a/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/clickup-connect-form.tsx b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/clickup-connect-form.tsx new file mode 100644 index 000000000..9f33c6ed9 --- /dev/null +++ b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/clickup-connect-form.tsx @@ -0,0 +1,385 @@ +"use client"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { Info } from "lucide-react"; +import type { FC } from "react"; +import { useRef, useState } from "react"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; +import { EnumConnectorName } from "@/contracts/enums/connector"; +import { DateRangeSelector } from "../../components/date-range-selector"; +import { getConnectorBenefits } from "../connector-benefits"; +import type { ConnectFormProps } from "../index"; + +const clickupConnectorFormSchema = z.object({ + name: z.string().min(3, { + message: "Connector name must be at least 3 characters.", + }), + api_token: z.string().min(10, { + message: "ClickUp API Token is required and must be valid.", + }), +}); + +type ClickUpConnectorFormValues = z.infer; + +export const ClickUpConnectForm: FC = ({ onSubmit, isSubmitting }) => { + const isSubmittingRef = useRef(false); + const [startDate, setStartDate] = useState(undefined); + const [endDate, setEndDate] = useState(undefined); + const [periodicEnabled, setPeriodicEnabled] = useState(false); + const [frequencyMinutes, setFrequencyMinutes] = useState("1440"); + const form = useForm({ + resolver: zodResolver(clickupConnectorFormSchema), + defaultValues: { + name: "ClickUp Connector", + api_token: "", + }, + }); + + const handleSubmit = async (values: ClickUpConnectorFormValues) => { + // Prevent multiple submissions + if (isSubmittingRef.current || isSubmitting) { + return; + } + + isSubmittingRef.current = true; + try { + await onSubmit({ + name: values.name, + connector_type: EnumConnectorName.CLICKUP_CONNECTOR, + config: { + CLICKUP_API_TOKEN: values.api_token, + }, + is_indexable: true, + last_indexed_at: null, + periodic_indexing_enabled: periodicEnabled, + indexing_frequency_minutes: periodicEnabled ? parseInt(frequencyMinutes, 10) : null, + next_scheduled_at: null, + startDate, + endDate, + periodicEnabled, + frequencyMinutes, + }); + } finally { + isSubmittingRef.current = false; + } + }; + + return ( +
+ + +
+ API Token Required + + You'll need a ClickUp API Token to use this connector. You can create one from{" "} + + ClickUp Settings + + +
+
+ +
+
+ + ( + + Connector Name + + + + + A friendly name to identify this connector. + + + + )} + /> + + ( + + ClickUp API Token + + + + + Your ClickUp API Token will be encrypted and stored securely. + + + + )} + /> + + {/* Indexing Configuration */} +
+

Indexing Configuration

+ + {/* Date Range Selector */} + + + {/* Periodic Sync Config */} +
+
+
+

Enable Periodic Sync

+

+ Automatically re-index at regular intervals +

+
+ +
+ + {periodicEnabled && ( +
+
+ + +
+
+ )} +
+
+ + +
+ + {/* What you get section */} + {getConnectorBenefits(EnumConnectorName.CLICKUP_CONNECTOR) && ( +
+

What you get with ClickUp integration:

+
    + {getConnectorBenefits(EnumConnectorName.CLICKUP_CONNECTOR)?.map((benefit) => ( +
  • {benefit}
  • + ))} +
+
+ )} + + {/* Documentation Section */} + + + + Documentation + + +
+

How it works

+

+ The ClickUp connector uses the ClickUp API to fetch all tasks and projects that your + API token has access to within your workspace. +

+
    +
  • + For follow up indexing runs, the connector retrieves tasks that have been updated + since the last indexing attempt. +
  • +
  • + Indexing is configured to run periodically, so updates should appear in your + search results within minutes. +
  • +
+
+ +
+
+

Authorization

+ + + API Token Required + + You need a ClickUp personal API token to use this connector. The token will be + used to read your ClickUp data. + + + +
+
+

+ Step 1: Get Your API Token +

+
    +
  1. Log in to your ClickUp account
  2. +
  3. Click your avatar in the upper-right corner and select "Settings"
  4. +
  5. In the sidebar, click "Apps"
  6. +
  7. + Under "API Token", click Generate or{" "} + Regenerate +
  8. +
  9. Copy the generated token (it typically starts with "pk_")
  10. +
  11. + Paste it in the form above. You can also visit{" "} + + ClickUp API Settings + {" "} + directly. +
  12. +
+
+ +
+

+ Step 2: Grant necessary access +

+

+ The API Token will have access to all tasks and projects that your user + account can see. Make sure your account has appropriate permissions for the + workspaces you want to index. +

+ + + Data Privacy + + Only tasks, comments, and basic metadata will be indexed. ClickUp + attachments and linked files are not indexed by this connector. + + +
+
+
+
+ +
+
+

Indexing

+
    +
  1. + Navigate to the Connector Dashboard and select the ClickUp{" "} + Connector. +
  2. +
  3. + Place your API Token in the form field. +
  4. +
  5. + Click Connect to establish the connection. +
  6. +
  7. Once connected, your ClickUp tasks will be indexed automatically.
  8. +
+ + + + What Gets Indexed + +

The ClickUp connector indexes the following data:

+
    +
  • Task names and descriptions
  • +
  • Task comments and discussion threads
  • +
  • Task status, priority, and assignee information
  • +
  • Project and workspace information
  • +
+
+
+
+
+
+
+
+
+ ); +}; diff --git a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/teams-config 2.tsx b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/teams-config 2.tsx new file mode 100644 index 000000000..ac08a6c03 --- /dev/null +++ b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/teams-config 2.tsx @@ -0,0 +1,29 @@ +"use client"; + +import { Info } from "lucide-react"; +import type { FC } from "react"; +import type { ConnectorConfigProps } from "../index"; + +export interface TeamsConfigProps extends ConnectorConfigProps { + onNameChange?: (name: string) => void; +} + +export const TeamsConfig: FC = () => { + return ( +
+
+
+ +
+
+

Microsoft Teams Access

+

+ SurfSense will index messages from Teams channels that you have access to. The app can + only read messages from teams and channels where you are a member. Make sure you're a + member of the teams you want to index before connecting. +

+
+
+
+ ); +}; diff --git a/surfsense_web/components/assistant-ui/connector-popup/views/connector-accounts-list-view 2.tsx b/surfsense_web/components/assistant-ui/connector-popup/views/connector-accounts-list-view 2.tsx new file mode 100644 index 000000000..e45f24d11 --- /dev/null +++ b/surfsense_web/components/assistant-ui/connector-popup/views/connector-accounts-list-view 2.tsx @@ -0,0 +1,189 @@ +"use client"; + +import { differenceInDays, differenceInMinutes, format, isToday, isYesterday } from "date-fns"; +import { ArrowLeft, Loader2, Plus } from "lucide-react"; +import type { FC } from "react"; +import { Button } from "@/components/ui/button"; +import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; +import type { SearchSourceConnector } from "@/contracts/types/connector.types"; +import type { LogActiveTask, LogSummary } from "@/contracts/types/log.types"; +import { cn } from "@/lib/utils"; +import { getConnectorDisplayName } from "../tabs/all-connectors-tab"; + +interface ConnectorAccountsListViewProps { + connectorType: string; + connectorTitle: string; + connectors: SearchSourceConnector[]; + indexingConnectorIds: Set; + logsSummary: LogSummary | undefined; + onBack: () => void; + onManage: (connector: SearchSourceConnector) => void; + onAddAccount: () => void; + isConnecting?: boolean; +} + +/** + * Format last indexed date with contextual messages + */ +function formatLastIndexedDate(dateString: string): string { + const date = new Date(dateString); + const now = new Date(); + const minutesAgo = differenceInMinutes(now, date); + const daysAgo = differenceInDays(now, date); + + if (minutesAgo < 1) { + return "Just now"; + } + + if (minutesAgo < 60) { + return `${minutesAgo} ${minutesAgo === 1 ? "minute" : "minutes"} ago`; + } + + if (isToday(date)) { + return `Today at ${format(date, "h:mm a")}`; + } + + if (isYesterday(date)) { + return `Yesterday at ${format(date, "h:mm a")}`; + } + + if (daysAgo < 7) { + return `${daysAgo} ${daysAgo === 1 ? "day" : "days"} ago`; + } + + return format(date, "MMM d, yyyy"); +} + +export const ConnectorAccountsListView: FC = ({ + connectorType, + connectorTitle, + connectors, + indexingConnectorIds, + logsSummary, + onBack, + onManage, + onAddAccount, + isConnecting = false, +}) => { + // Filter connectors to only show those of this type + const typeConnectors = connectors.filter((c) => c.connector_type === connectorType); + + return ( +
+ {/* Header */} +
+
+
+ +
+
+ {getConnectorIcon(connectorType, "size-5")} +
+
+

{connectorTitle} Accounts

+

+ {typeConnectors.length} connected account{typeConnectors.length !== 1 ? "s" : ""} +

+
+
+
+ {/* Add Account Button with dashed border */} + +
+
+ + {/* Content */} +
+ {/* Connected Accounts Grid */} +
+ {typeConnectors.map((connector) => { + const isIndexing = indexingConnectorIds.has(connector.id); + const activeTask = logsSummary?.active_tasks?.find( + (task: LogActiveTask) => task.connector_id === connector.id + ); + + return ( +
+
+ {getConnectorIcon(connector.connector_type, "size-6")} +
+
+

+ {getConnectorDisplayName(connector.name)} +

+ {isIndexing ? ( +

+ + Indexing... + {activeTask?.message && ( + + • {activeTask.message} + + )} +

+ ) : ( +

+ {connector.last_indexed_at + ? `Last indexed: ${formatLastIndexedDate(connector.last_indexed_at)}` + : "Never indexed"} +

+ )} +
+ +
+ ); + })} +
+
+
+ ); +}; diff --git a/surfsense_web/components/layout/index.ts b/surfsense_web/components/layout/index.ts index 745075b6f..4fe2975c1 100644 --- a/surfsense_web/components/layout/index.ts +++ b/surfsense_web/components/layout/index.ts @@ -8,7 +8,7 @@ export type { PageUsage, SidebarSectionProps, User, - Workspace, + SearchSpace, } from "./types/layout.types"; export { ChatListItem, @@ -26,5 +26,5 @@ export { SidebarHeader, SidebarSection, SidebarUserProfile, - WorkspaceAvatar, + SearchSpaceAvatar, } from "./ui"; diff --git a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx index ea750a365..b54f2b2fd 100644 --- a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx +++ b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx @@ -25,7 +25,7 @@ import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { deleteThread, fetchThreads } from "@/lib/chat/thread-persistence"; import { resetUser, trackLogout } from "@/lib/posthog/events"; import { cacheKeys } from "@/lib/query-client/cache-keys"; -import type { ChatItem, NavItem, NoteItem, Workspace } from "../types/layout.types"; +import type { ChatItem, NavItem, NoteItem, SearchSpace } from "../types/layout.types"; import { LayoutShell } from "../ui/shell"; import { AllChatsSidebar } from "../ui/sidebar/AllChatsSidebar"; import { AllNotesSidebar } from "../ui/sidebar/AllNotesSidebar"; @@ -123,8 +123,8 @@ export function LayoutDataProvider({ } | null>(null); const [isDeletingNote, setIsDeletingNote] = useState(false); - // Transform workspaces (API returns array directly, not { items: [...] }) - const workspaces: Workspace[] = useMemo(() => { + // Transform search spaces (API returns array directly, not { items: [...] }) + const searchSpaces: SearchSpace[] = useMemo(() => { if (!searchSpacesData || !Array.isArray(searchSpacesData)) return []; return searchSpacesData.map((space) => ({ id: space.id, @@ -135,8 +135,8 @@ export function LayoutDataProvider({ })); }, [searchSpacesData]); - // Use searchSpace query result for current workspace (more reliable than finding in list) - const activeWorkspace: Workspace | null = searchSpace + // Use searchSpace query result for active search space (more reliable than finding in list) + const activeSearchSpace: SearchSpace | null = searchSpace ? { id: searchSpace.id, name: searchSpace.name, @@ -196,18 +196,18 @@ export function LayoutDataProvider({ ); // Handlers - const handleWorkspaceSelect = useCallback( + const handleSearchSpaceSelect = useCallback( (id: number) => { router.push(`/dashboard/${id}/new-chat`); }, [router] ); - const handleAddWorkspace = useCallback(() => { + const handleAddSearchSpace = useCallback(() => { router.push("/dashboard/searchspaces"); }, [router]); - const handleSeeAllWorkspaces = useCallback(() => { + const handleSeeAllSearchSpaces = useCallback(() => { router.push("/dashboard"); }, [router]); @@ -266,7 +266,7 @@ export function LayoutDataProvider({ router.push(`/dashboard/${searchSpaceId}/settings`); }, [router, searchSpaceId]); - const handleInviteMembers = useCallback(() => { + const handleManageMembers = useCallback(() => { router.push(`/dashboard/${searchSpaceId}/team`); }, [router, searchSpaceId]); @@ -347,11 +347,11 @@ export function LayoutDataProvider({ return ( <> void; - onAddWorkspace: () => void; + searchSpaces: SearchSpace[]; + activeSearchSpaceId: number | null; + onSearchSpaceSelect: (id: number) => void; + onAddSearchSpace: () => void; className?: string; } export interface SidebarHeaderProps { - workspace: Workspace | null; + searchSpace: SearchSpace | null; onSettings?: () => void; } @@ -94,15 +94,15 @@ export interface SidebarUserProfileProps { user: User; searchSpaceId?: string; onSettings?: () => void; - onInviteMembers?: () => void; - onSwitchWorkspace?: () => void; + onManageMembers?: () => void; + onSwitchSearchSpace?: () => void; onToggleTheme?: () => void; onLogout?: () => void; theme?: string; } export interface SidebarProps { - workspace: Workspace | null; + searchSpace: SearchSpace | null; searchSpaceId?: string; navItems: NavItem[]; chats: ChatItem[]; @@ -120,8 +120,8 @@ export interface SidebarProps { user: User; theme?: string; onSettings?: () => void; - onInviteMembers?: () => void; - onSwitchWorkspace?: () => void; + onManageMembers?: () => void; + onSeeAllSearchSpaces?: () => void; onToggleTheme?: () => void; onLogout?: () => void; pageUsage?: PageUsage; @@ -129,10 +129,10 @@ export interface SidebarProps { } export interface LayoutShellProps { - workspaces: Workspace[]; - activeWorkspaceId: number | null; - onWorkspaceSelect: (id: number) => void; - onAddWorkspace: () => void; + searchSpaces: SearchSpace[]; + activeSearchSpaceId: number | null; + onSearchSpaceSelect: (id: number) => void; + onAddSearchSpace: () => void; sidebarProps: Omit; children: React.ReactNode; className?: string; diff --git a/surfsense_web/components/layout/ui/icon-rail/IconRail.tsx b/surfsense_web/components/layout/ui/icon-rail/IconRail.tsx index 0d6b39cdc..3e8b14ba9 100644 --- a/surfsense_web/components/layout/ui/icon-rail/IconRail.tsx +++ b/surfsense_web/components/layout/ui/icon-rail/IconRail.tsx @@ -5,34 +5,34 @@ import { Button } from "@/components/ui/button"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; -import type { Workspace } from "../../types/layout.types"; -import { WorkspaceAvatar } from "./WorkspaceAvatar"; +import type { SearchSpace } from "../../types/layout.types"; +import { SearchSpaceAvatar } from "./SearchSpaceAvatar"; interface IconRailProps { - workspaces: Workspace[]; - activeWorkspaceId: number | null; - onWorkspaceSelect: (id: number) => void; - onAddWorkspace: () => void; + searchSpaces: SearchSpace[]; + activeSearchSpaceId: number | null; + onSearchSpaceSelect: (id: number) => void; + onAddSearchSpace: () => void; className?: string; } export function IconRail({ - workspaces, - activeWorkspaceId, - onWorkspaceSelect, - onAddWorkspace, + searchSpaces, + activeSearchSpaceId, + onSearchSpaceSelect, + onAddSearchSpace, className, }: IconRailProps) { return (
- {workspaces.map((workspace) => ( - onWorkspaceSelect(workspace.id)} + {searchSpaces.map((searchSpace) => ( + onSearchSpaceSelect(searchSpace.id)} size="md" /> ))} @@ -42,15 +42,15 @@ export function IconRail({ - Add workspace + Add search space
diff --git a/surfsense_web/components/layout/ui/icon-rail/SearchSpaceAvatar.tsx b/surfsense_web/components/layout/ui/icon-rail/SearchSpaceAvatar.tsx new file mode 100644 index 000000000..397076cb6 --- /dev/null +++ b/surfsense_web/components/layout/ui/icon-rail/SearchSpaceAvatar.tsx @@ -0,0 +1,72 @@ +"use client"; + +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; + +interface SearchSpaceAvatarProps { + name: string; + isActive?: boolean; + onClick?: () => void; + size?: "sm" | "md"; +} + +/** + * Generates a consistent color based on search space name + */ +function stringToColor(str: string): string { + let hash = 0; + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + const colors = [ + "#6366f1", // indigo + "#22c55e", // green + "#f59e0b", // amber + "#ef4444", // red + "#8b5cf6", // violet + "#06b6d4", // cyan + "#ec4899", // pink + "#14b8a6", // teal + ]; + return colors[Math.abs(hash) % colors.length]; +} + +/** + * Gets initials from search space name (max 2 chars) + */ +function getInitials(name: string): string { + const words = name.trim().split(/\s+/); + if (words.length >= 2) { + return (words[0][0] + words[1][0]).toUpperCase(); + } + return name.slice(0, 2).toUpperCase(); +} + +export function SearchSpaceAvatar({ name, isActive, onClick, size = "md" }: SearchSpaceAvatarProps) { + const bgColor = stringToColor(name); + const initials = getInitials(name); + const sizeClasses = size === "sm" ? "h-8 w-8 text-xs" : "h-10 w-10 text-sm"; + + return ( + + + + + + {name} + + + ); +} diff --git a/surfsense_web/components/layout/ui/icon-rail/WorkspaceAvatar.tsx b/surfsense_web/components/layout/ui/icon-rail/WorkspaceAvatar.tsx index 1c4798d2a..397076cb6 100644 --- a/surfsense_web/components/layout/ui/icon-rail/WorkspaceAvatar.tsx +++ b/surfsense_web/components/layout/ui/icon-rail/WorkspaceAvatar.tsx @@ -3,7 +3,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; -interface WorkspaceAvatarProps { +interface SearchSpaceAvatarProps { name: string; isActive?: boolean; onClick?: () => void; @@ -11,7 +11,7 @@ interface WorkspaceAvatarProps { } /** - * Generates a consistent color based on workspace name + * Generates a consistent color based on search space name */ function stringToColor(str: string): string { let hash = 0; @@ -32,7 +32,7 @@ function stringToColor(str: string): string { } /** - * Gets initials from workspace name (max 2 chars) + * Gets initials from search space name (max 2 chars) */ function getInitials(name: string): string { const words = name.trim().split(/\s+/); @@ -42,7 +42,7 @@ function getInitials(name: string): string { return name.slice(0, 2).toUpperCase(); } -export function WorkspaceAvatar({ name, isActive, onClick, size = "md" }: WorkspaceAvatarProps) { +export function SearchSpaceAvatar({ name, isActive, onClick, size = "md" }: SearchSpaceAvatarProps) { const bgColor = stringToColor(name); const initials = getInitials(name); const sizeClasses = size === "sm" ? "h-8 w-8 text-xs" : "h-10 w-10 text-sm"; diff --git a/surfsense_web/components/layout/ui/icon-rail/index.ts b/surfsense_web/components/layout/ui/icon-rail/index.ts index 0e7e8cd29..b635e7273 100644 --- a/surfsense_web/components/layout/ui/icon-rail/index.ts +++ b/surfsense_web/components/layout/ui/icon-rail/index.ts @@ -1,3 +1,3 @@ export { IconRail } from "./IconRail"; export { NavIcon } from "./NavIcon"; -export { WorkspaceAvatar } from "./WorkspaceAvatar"; +export { SearchSpaceAvatar } from "./SearchSpaceAvatar"; diff --git a/surfsense_web/components/layout/ui/index.ts b/surfsense_web/components/layout/ui/index.ts index 74b1e9240..31e288561 100644 --- a/surfsense_web/components/layout/ui/index.ts +++ b/surfsense_web/components/layout/ui/index.ts @@ -1,5 +1,5 @@ export { Header } from "./header"; -export { IconRail, NavIcon, WorkspaceAvatar } from "./icon-rail"; +export { IconRail, NavIcon, SearchSpaceAvatar } from "./icon-rail"; export { LayoutShell } from "./shell"; export { ChatListItem, diff --git a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx index 0d7b24113..50f963fb9 100644 --- a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx +++ b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx @@ -11,18 +11,18 @@ import type { NoteItem, PageUsage, User, - Workspace, + SearchSpace, } from "../../types/layout.types"; import { Header } from "../header"; import { IconRail } from "../icon-rail"; import { MobileSidebar, MobileSidebarTrigger, Sidebar } from "../sidebar"; interface LayoutShellProps { - workspaces: Workspace[]; - activeWorkspaceId: number | null; - onWorkspaceSelect: (id: number) => void; - onAddWorkspace: () => void; - workspace: Workspace | null; + searchSpaces: SearchSpace[]; + activeSearchSpaceId: number | null; + onSearchSpaceSelect: (id: number) => void; + onAddSearchSpace: () => void; + searchSpace: SearchSpace | null; navItems: NavItem[]; onNavItemClick?: (item: NavItem) => void; chats: ChatItem[]; @@ -39,8 +39,8 @@ interface LayoutShellProps { onViewAllNotes?: () => void; user: User; onSettings?: () => void; - onInviteMembers?: () => void; - onSeeAllWorkspaces?: () => void; + onManageMembers?: () => void; + onSeeAllSearchSpaces?: () => void; onLogout?: () => void; pageUsage?: PageUsage; breadcrumb?: React.ReactNode; @@ -54,11 +54,11 @@ interface LayoutShellProps { } export function LayoutShell({ - workspaces, - activeWorkspaceId, - onWorkspaceSelect, - onAddWorkspace, - workspace, + searchSpaces, + activeSearchSpaceId, + onSearchSpaceSelect, + onAddSearchSpace, + searchSpace, navItems, onNavItemClick, chats, @@ -75,8 +75,8 @@ export function LayoutShell({ onViewAllNotes, user, onSettings, - onInviteMembers, - onSeeAllWorkspaces, + onManageMembers, + onSeeAllSearchSpaces, onLogout, pageUsage, breadcrumb, @@ -108,11 +108,11 @@ export function LayoutShell({ @@ -149,16 +149,16 @@ export function LayoutShell({
void; - workspaces: Workspace[]; - activeWorkspaceId: number | null; - onWorkspaceSelect: (id: number) => void; - onAddWorkspace: () => void; - workspace: Workspace | null; + searchSpaces: SearchSpace[]; + activeSearchSpaceId: number | null; + onSearchSpaceSelect: (id: number) => void; + onAddSearchSpace: () => void; + searchSpace: SearchSpace | null; navItems: NavItem[]; onNavItemClick?: (item: NavItem) => void; chats: ChatItem[]; @@ -39,8 +39,8 @@ interface MobileSidebarProps { onViewAllNotes?: () => void; user: User; onSettings?: () => void; - onInviteMembers?: () => void; - onSeeAllWorkspaces?: () => void; + onManageMembers?: () => void; + onSeeAllSearchSpaces?: () => void; onLogout?: () => void; pageUsage?: PageUsage; } @@ -57,11 +57,11 @@ export function MobileSidebarTrigger({ onClick }: { onClick: () => void }) { export function MobileSidebar({ isOpen, onOpenChange, - workspaces, - activeWorkspaceId, - onWorkspaceSelect, - onAddWorkspace, - workspace, + searchSpaces, + activeSearchSpaceId, + onSearchSpaceSelect, + onAddSearchSpace, + searchSpace, navItems, onNavItemClick, chats, @@ -78,13 +78,13 @@ export function MobileSidebar({ onViewAllNotes, user, onSettings, - onInviteMembers, - onSeeAllWorkspaces, + onManageMembers, + onSeeAllSearchSpaces, onLogout, pageUsage, }: MobileSidebarProps) { - const handleWorkspaceSelect = (id: number) => { - onWorkspaceSelect(id); + const handleSearchSpaceSelect = (id: number) => { + onSearchSpaceSelect(id); }; const handleNavItemClick = (item: NavItem) => { @@ -110,17 +110,17 @@ export function MobileSidebar({
void; navItems: NavItem[]; @@ -43,15 +43,15 @@ interface SidebarProps { onViewAllNotes?: () => void; user: User; onSettings?: () => void; - onInviteMembers?: () => void; - onSeeAllWorkspaces?: () => void; + onManageMembers?: () => void; + onSeeAllSearchSpaces?: () => void; onLogout?: () => void; pageUsage?: PageUsage; className?: string; } export function Sidebar({ - workspace, + searchSpace, isCollapsed = false, onToggleCollapse, navItems, @@ -70,8 +70,8 @@ export function Sidebar({ onViewAllNotes, user, onSettings, - onInviteMembers, - onSeeAllWorkspaces, + onManageMembers, + onSeeAllSearchSpaces, onLogout, pageUsage, className, @@ -86,7 +86,7 @@ export function Sidebar({ className )} > - {/* Header - workspace name or collapse button when collapsed */} + {/* Header - search space name or collapse button when collapsed */} {isCollapsed ? (
void; - onInviteMembers?: () => void; - onSeeAllWorkspaces?: () => void; + onManageMembers?: () => void; + onSeeAllSearchSpaces?: () => void; className?: string; } export function SidebarHeader({ - workspace, + searchSpace, isCollapsed, onSettings, - onInviteMembers, - onSeeAllWorkspaces, + onManageMembers, + onSeeAllSearchSpaces, className, }: SidebarHeaderProps) { const t = useTranslations("sidebar"); @@ -43,24 +43,24 @@ export function SidebarHeader({ isCollapsed ? "w-10" : "w-50" )} > - {workspace?.name ?? t("select_workspace")} + {searchSpace?.name ?? t("select_search_space")} - - - {t("invite_members")} + + + {t("manage_members")} - {t("workspace_settings")} + {t("search_space_settings")} - + - {t("see_all_workspaces")} + {t("see_all_search_spaces")} diff --git a/surfsense_web/messages/en.json b/surfsense_web/messages/en.json index b803d4b69..8b0164211 100644 --- a/surfsense_web/messages/en.json +++ b/surfsense_web/messages/en.json @@ -625,9 +625,13 @@ "error_archiving_chat": "Failed to archive chat", "new_chat": "New chat", "select_workspace": "Select Workspace", + "select_search_space": "Select Search Space", "invite_members": "Invite members", + "manage_members": "Manage members", "workspace_settings": "Workspace settings", + "search_space_settings": "Search space settings", "see_all_workspaces": "See all search spaces", + "see_all_search_spaces": "See all search spaces", "expand_sidebar": "Expand sidebar", "collapse_sidebar": "Collapse sidebar", "logout": "Logout" diff --git a/surfsense_web/messages/zh.json b/surfsense_web/messages/zh.json index fa690bf39..ee04baad5 100644 --- a/surfsense_web/messages/zh.json +++ b/surfsense_web/messages/zh.json @@ -619,9 +619,13 @@ "add_note": "添加笔记", "new_chat": "新对话", "select_workspace": "选择工作空间", + "select_search_space": "选择搜索空间", "invite_members": "邀请成员", + "manage_members": "管理成员", "workspace_settings": "工作空间设置", + "search_space_settings": "搜索空间设置", "see_all_workspaces": "查看所有搜索空间", + "see_all_search_spaces": "查看所有搜索空间", "expand_sidebar": "展开侧边栏", "collapse_sidebar": "收起侧边栏", "logout": "退出登录"