mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-23 19:05:16 +02:00
refactor: enhance ConnectToolsBanner functionality and update sidebar navigation handling
This commit is contained in:
parent
a49ee05456
commit
88a43cdd65
3 changed files with 94 additions and 66 deletions
|
|
@ -261,7 +261,10 @@ const BANNER_CONNECTORS = [
|
||||||
|
|
||||||
const BANNER_DISMISSED_KEY = "surfsense-connect-tools-banner-dismissed";
|
const BANNER_DISMISSED_KEY = "surfsense-connect-tools-banner-dismissed";
|
||||||
|
|
||||||
const ConnectToolsBanner: FC<{ isThreadEmpty: boolean }> = ({ isThreadEmpty }) => {
|
const ConnectToolsBanner: FC<{
|
||||||
|
isThreadEmpty: boolean;
|
||||||
|
onVisibleChange?: (visible: boolean) => void;
|
||||||
|
}> = ({ isThreadEmpty, onVisibleChange }) => {
|
||||||
const { data: connectors } = useAtomValue(connectorsAtom);
|
const { data: connectors } = useAtomValue(connectorsAtom);
|
||||||
const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom);
|
const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom);
|
||||||
const [dismissed, setDismissed] = useState(() => {
|
const [dismissed, setDismissed] = useState(() => {
|
||||||
|
|
@ -270,8 +273,13 @@ const ConnectToolsBanner: FC<{ isThreadEmpty: boolean }> = ({ isThreadEmpty }) =
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasConnectors = (connectors?.length ?? 0) > 0;
|
const hasConnectors = (connectors?.length ?? 0) > 0;
|
||||||
|
const isVisible = !dismissed && !hasConnectors && isThreadEmpty;
|
||||||
|
|
||||||
if (dismissed || hasConnectors || !isThreadEmpty) return null;
|
useEffect(() => {
|
||||||
|
onVisibleChange?.(isVisible);
|
||||||
|
}, [isVisible, onVisibleChange]);
|
||||||
|
|
||||||
|
if (!isVisible) return null;
|
||||||
|
|
||||||
const handleDismiss = (e: React.MouseEvent) => {
|
const handleDismiss = (e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -280,42 +288,41 @@ const ConnectToolsBanner: FC<{ isThreadEmpty: boolean }> = ({ isThreadEmpty }) =
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-t border-border/50">
|
<div className="relative z-0 -mt-5 flex min-w-0 items-center gap-2 rounded-b-3xl border border-input bg-muted/40 px-4 pt-7 pb-3 shadow-sm shadow-black/5 dark:shadow-black/10">
|
||||||
<div className="flex w-full items-center gap-2.5 px-4 py-2.5">
|
<Button
|
||||||
<Button
|
type="button"
|
||||||
type="button"
|
variant="ghost"
|
||||||
variant="ghost"
|
size="sm"
|
||||||
size="sm"
|
className="h-7 min-w-0 cursor-pointer justify-start gap-2 rounded-md px-0 text-[13px] font-normal text-muted-foreground select-none hover:bg-transparent hover:text-foreground"
|
||||||
className="h-auto flex-1 justify-start gap-2.5 px-0 py-0 text-left cursor-pointer select-none hover:bg-transparent"
|
onClick={() => setConnectorDialogOpen(true)}
|
||||||
onClick={() => setConnectorDialogOpen(true)}
|
>
|
||||||
>
|
<Unplug className="size-4 shrink-0" />
|
||||||
<Unplug className="size-4 text-muted-foreground shrink-0" />
|
<span className="truncate">Connect your tools</span>
|
||||||
<span className="text-[13px] text-muted-foreground/80 flex-1">Connect your tools</span>
|
</Button>
|
||||||
<AvatarGroup className="shrink-0">
|
<div className="min-w-0 flex-1" />
|
||||||
{BANNER_CONNECTORS.map(({ type }, i) => (
|
<AvatarGroup className="shrink-0">
|
||||||
<Avatar
|
{BANNER_CONNECTORS.map(({ type }, i) => (
|
||||||
key={type}
|
<Avatar
|
||||||
className="size-6"
|
key={type}
|
||||||
style={{ zIndex: BANNER_CONNECTORS.length - i }}
|
className="size-5"
|
||||||
>
|
style={{ zIndex: BANNER_CONNECTORS.length - i }}
|
||||||
<AvatarFallback className="bg-muted text-[10px]">
|
>
|
||||||
{getConnectorIcon(type, "size-3.5")}
|
<AvatarFallback className="bg-accent text-[10px]">
|
||||||
</AvatarFallback>
|
{getConnectorIcon(type, "size-3")}
|
||||||
</Avatar>
|
</AvatarFallback>
|
||||||
))}
|
</Avatar>
|
||||||
</AvatarGroup>
|
))}
|
||||||
</Button>
|
</AvatarGroup>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleDismiss}
|
onClick={handleDismiss}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="size-auto shrink-0 ml-0.5 -mr-1 p-1.5 text-muted-foreground/40 hover:bg-transparent hover:text-accent-foreground cursor-pointer"
|
className="size-7 shrink-0 cursor-pointer rounded-md text-muted-foreground hover:bg-transparent hover:text-foreground"
|
||||||
aria-label="Dismiss"
|
aria-label="Dismiss"
|
||||||
>
|
>
|
||||||
<X className="size-3.5" />
|
<X className="size-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -426,6 +433,7 @@ const Composer: FC = () => {
|
||||||
|
|
||||||
const isThreadEmpty = useAuiState(({ thread }) => thread.isEmpty);
|
const isThreadEmpty = useAuiState(({ thread }) => thread.isEmpty);
|
||||||
const isThreadRunning = useAuiState(({ thread }) => thread.isRunning);
|
const isThreadRunning = useAuiState(({ thread }) => thread.isRunning);
|
||||||
|
const [connectToolsTrayVisible, setConnectToolsTrayVisible] = useState(false);
|
||||||
|
|
||||||
const currentPlaceholder = COMPOSER_PLACEHOLDER;
|
const currentPlaceholder = COMPOSER_PLACEHOLDER;
|
||||||
|
|
||||||
|
|
@ -735,32 +743,42 @@ const Composer: FC = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="aui-composer-attachment-dropzone flex w-full flex-col overflow-hidden rounded-3xl border-input bg-muted pt-2 shadow-sm shadow-black/5 outline-none transition-shadow dark:shadow-black/10">
|
<div className="flex w-full flex-col">
|
||||||
<PendingScreenImageStrip />
|
<div
|
||||||
{clipboardInitialText && (
|
className={cn(
|
||||||
<ClipboardChip
|
"aui-composer-attachment-dropzone relative z-10 flex w-full flex-col overflow-hidden rounded-3xl border border-input bg-muted pt-2 shadow-sm shadow-black/5 outline-none transition-shadow dark:shadow-black/10",
|
||||||
text={clipboardInitialText}
|
connectToolsTrayVisible && "rounded-b-3xl shadow-none dark:shadow-none"
|
||||||
onDismiss={() => setClipboardInitialText(undefined)}
|
)}
|
||||||
/>
|
>
|
||||||
)}
|
<PendingScreenImageStrip />
|
||||||
<div className="aui-composer-input-wrapper px-4 pt-3 pb-6">
|
{clipboardInitialText && (
|
||||||
<InlineMentionEditor
|
<ClipboardChip
|
||||||
ref={editorRef}
|
text={clipboardInitialText}
|
||||||
placeholder={currentPlaceholder}
|
onDismiss={() => setClipboardInitialText(undefined)}
|
||||||
onMentionTrigger={handleMentionTrigger}
|
/>
|
||||||
onMentionClose={handleMentionClose}
|
)}
|
||||||
onActionTrigger={handleActionTrigger}
|
<div className="aui-composer-input-wrapper px-4 pt-3 pb-6">
|
||||||
onActionClose={handleActionClose}
|
<InlineMentionEditor
|
||||||
onChange={handleEditorChange}
|
ref={editorRef}
|
||||||
onDocumentRemove={handleDocumentRemove}
|
placeholder={currentPlaceholder}
|
||||||
onSubmit={handleSubmit}
|
onMentionTrigger={handleMentionTrigger}
|
||||||
onKeyDown={handleKeyDown}
|
onMentionClose={handleMentionClose}
|
||||||
className="min-h-[24px]"
|
onActionTrigger={handleActionTrigger}
|
||||||
/>
|
onActionClose={handleActionClose}
|
||||||
|
onChange={handleEditorChange}
|
||||||
|
onDocumentRemove={handleDocumentRemove}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
className="min-h-[24px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ComposerAction isBlockedByOtherUser={isBlockedByOtherUser} />
|
||||||
|
<ConnectorIndicator showTrigger={false} />
|
||||||
</div>
|
</div>
|
||||||
<ComposerAction isBlockedByOtherUser={isBlockedByOtherUser} />
|
<ConnectToolsBanner
|
||||||
<ConnectorIndicator showTrigger={false} />
|
isThreadEmpty={isThreadEmpty}
|
||||||
<ConnectToolsBanner isThreadEmpty={isThreadEmpty} />
|
onVisibleChange={setConnectToolsTrayVisible}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ComposerPrimitive.Root>
|
</ComposerPrimitive.Root>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,7 @@ export function MobileSidebar({
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
onNavigate={() => onOpenChange(false)}
|
||||||
announcementUnreadCount={announcementUnreadCount}
|
announcementUnreadCount={announcementUnreadCount}
|
||||||
onLogout={onLogout}
|
onLogout={onLogout}
|
||||||
pageUsage={pageUsage}
|
pageUsage={pageUsage}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { CreditCard, SquarePen, Zap } from "lucide-react";
|
import { CreditCard, Dot, SquarePen, Zap } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
@ -83,6 +83,7 @@ interface SidebarProps {
|
||||||
onManageMembers?: () => void;
|
onManageMembers?: () => void;
|
||||||
onUserSettings?: () => void;
|
onUserSettings?: () => void;
|
||||||
onAnnouncements?: () => void;
|
onAnnouncements?: () => void;
|
||||||
|
onNavigate?: () => void;
|
||||||
announcementUnreadCount?: number;
|
announcementUnreadCount?: number;
|
||||||
onLogout?: () => void;
|
onLogout?: () => void;
|
||||||
pageUsage?: PageUsage;
|
pageUsage?: PageUsage;
|
||||||
|
|
@ -119,6 +120,7 @@ export function Sidebar({
|
||||||
onManageMembers,
|
onManageMembers,
|
||||||
onUserSettings,
|
onUserSettings,
|
||||||
onAnnouncements,
|
onAnnouncements,
|
||||||
|
onNavigate,
|
||||||
announcementUnreadCount = 0,
|
announcementUnreadCount = 0,
|
||||||
onLogout,
|
onLogout,
|
||||||
pageUsage,
|
pageUsage,
|
||||||
|
|
@ -344,6 +346,7 @@ export function Sidebar({
|
||||||
pageUsage={pageUsage}
|
pageUsage={pageUsage}
|
||||||
isCollapsed={isCollapsed}
|
isCollapsed={isCollapsed}
|
||||||
hasNavSectionAbove={footerNavItems.length > 0}
|
hasNavSectionAbove={footerNavItems.length > 0}
|
||||||
|
onNavigate={onNavigate}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{renderUserProfile && (
|
{renderUserProfile && (
|
||||||
|
|
@ -367,10 +370,12 @@ function SidebarUsageFooter({
|
||||||
pageUsage,
|
pageUsage,
|
||||||
isCollapsed,
|
isCollapsed,
|
||||||
hasNavSectionAbove = false,
|
hasNavSectionAbove = false,
|
||||||
|
onNavigate,
|
||||||
}: {
|
}: {
|
||||||
pageUsage?: PageUsage;
|
pageUsage?: PageUsage;
|
||||||
isCollapsed: boolean;
|
isCollapsed: boolean;
|
||||||
hasNavSectionAbove?: boolean;
|
hasNavSectionAbove?: boolean;
|
||||||
|
onNavigate?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const searchSpaceId = params?.search_space_id ?? "";
|
const searchSpaceId = params?.search_space_id ?? "";
|
||||||
|
|
@ -424,6 +429,7 @@ function SidebarUsageFooter({
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<Link
|
<Link
|
||||||
href={`/dashboard/${searchSpaceId}/more-pages`}
|
href={`/dashboard/${searchSpaceId}/more-pages`}
|
||||||
|
onClick={onNavigate}
|
||||||
className="group flex w-full items-center justify-between rounded-md px-1.5 py-1 transition-colors hover:bg-accent"
|
className="group flex w-full items-center justify-between rounded-md px-1.5 py-1 transition-colors hover:bg-accent"
|
||||||
>
|
>
|
||||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground group-hover:text-accent-foreground">
|
<span className="flex items-center gap-1.5 text-xs text-muted-foreground group-hover:text-accent-foreground">
|
||||||
|
|
@ -436,14 +442,17 @@ function SidebarUsageFooter({
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={`/dashboard/${searchSpaceId}/buy-more`}
|
href={`/dashboard/${searchSpaceId}/buy-more`}
|
||||||
|
onClick={onNavigate}
|
||||||
className="group flex w-full items-center justify-between rounded-md px-1.5 py-1 transition-colors hover:bg-accent"
|
className="group flex w-full items-center justify-between rounded-md px-1.5 py-1 transition-colors hover:bg-accent"
|
||||||
>
|
>
|
||||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground group-hover:text-accent-foreground">
|
<span className="flex items-center gap-1.5 text-xs text-muted-foreground group-hover:text-accent-foreground">
|
||||||
<CreditCard className="h-3 w-3 shrink-0" />
|
<CreditCard className="h-3 w-3 shrink-0" />
|
||||||
Buy More
|
Buy More
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[10px] font-medium text-muted-foreground">
|
<span className="flex items-center text-[10px] font-medium text-muted-foreground">
|
||||||
$1/1k · $1/1M
|
$1/1k
|
||||||
|
<Dot className="h-3 w-3" />
|
||||||
|
$1/1M
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue