mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-14 22:52:15 +02:00
feat(sidebar): enhance document filtering with Drawer component
- Integrated Drawer component in EmbeddedDocumentsMenu for mobile-friendly document type filtering. - Updated UI to improve accessibility and user experience with new button interactions and layout adjustments. - Refactored document type display logic to streamline filtering options and enhance visual clarity.
This commit is contained in:
parent
2461353312
commit
b911a36bcf
2 changed files with 105 additions and 44 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
import { useQuery } from "@rocicorp/zero/react";
|
import { useQuery } from "@rocicorp/zero/react";
|
||||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||||
import {
|
import {
|
||||||
|
Check,
|
||||||
FolderInput,
|
FolderInput,
|
||||||
FolderPlus,
|
FolderPlus,
|
||||||
FolderSync,
|
FolderSync,
|
||||||
|
|
@ -50,6 +51,7 @@ import {
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
|
|
@ -69,6 +71,7 @@ import { EnumConnectorName } from "@/contracts/enums/connector";
|
||||||
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
|
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
|
||||||
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
||||||
import type { DocumentTypeEnum } from "@/contracts/types/document.types";
|
import type { DocumentTypeEnum } from "@/contracts/types/document.types";
|
||||||
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
import { useElectronAPI, usePlatform } from "@/hooks/use-platform";
|
import { useElectronAPI, usePlatform } from "@/hooks/use-platform";
|
||||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||||
import { foldersApiService } from "@/lib/apis/folders-api.service";
|
import { foldersApiService } from "@/lib/apis/folders-api.service";
|
||||||
|
|
@ -127,60 +130,111 @@ export function EmbeddedDocumentsMenu({
|
||||||
onToggleType: (type: DocumentTypeEnum, checked: boolean) => void;
|
onToggleType: (type: DocumentTypeEnum, checked: boolean) => void;
|
||||||
onCreateFolder: () => void;
|
onCreateFolder: () => void;
|
||||||
}) {
|
}) {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
const [filterDrawerOpen, setFilterDrawerOpen] = useState(false);
|
||||||
const documentTypes = useMemo(
|
const documentTypes = useMemo(
|
||||||
() => Object.keys(typeCounts).sort() as DocumentTypeEnum[],
|
() => Object.keys(typeCounts).sort() as DocumentTypeEnum[],
|
||||||
[typeCounts]
|
[typeCounts]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenu>
|
||||||
<Button
|
<DropdownMenuTrigger asChild>
|
||||||
type="button"
|
<Button
|
||||||
variant="ghost"
|
type="button"
|
||||||
size="icon"
|
variant="ghost"
|
||||||
className="relative h-7 w-7 text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
size="icon"
|
||||||
aria-label="Document actions"
|
className="relative h-7 w-7 text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
||||||
|
aria-label="Document actions"
|
||||||
|
>
|
||||||
|
<SlidersVertical className="h-3.5 w-3.5" />
|
||||||
|
{activeTypes.length > 0 ? (
|
||||||
|
<span className="absolute right-0.5 top-0.5 h-1.5 w-1.5 rounded-full bg-primary" />
|
||||||
|
) : null}
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-44">
|
||||||
|
<DropdownMenuItem onSelect={onCreateFolder}>
|
||||||
|
<FolderPlus className="h-4 w-4" />
|
||||||
|
New folder
|
||||||
|
</DropdownMenuItem>
|
||||||
|
{isMobile ? (
|
||||||
|
<DropdownMenuItem onSelect={() => setFilterDrawerOpen(true)}>
|
||||||
|
<ListFilter className="h-4 w-4" />
|
||||||
|
<span className="flex-1">Filter by type</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
) : (
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger>
|
||||||
|
<ListFilter className="h-4 w-4" />
|
||||||
|
Filter by type
|
||||||
|
</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuSubContent className="w-52 max-h-72 overflow-y-auto">
|
||||||
|
{documentTypes.length > 0 ? (
|
||||||
|
documentTypes.map((type) => (
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
key={type}
|
||||||
|
checked={activeTypes.includes(type)}
|
||||||
|
onCheckedChange={(checked) => onToggleType(type, checked === true)}
|
||||||
|
onSelect={(event) => event.preventDefault()}
|
||||||
|
>
|
||||||
|
{getDocumentTypeIcon(type, "h-4 w-4")}
|
||||||
|
<span className="min-w-0 flex-1 truncate">{getDocumentTypeLabel(type)}</span>
|
||||||
|
<span className="ml-auto text-xs text-muted-foreground">
|
||||||
|
{typeCounts[type] ?? 0}
|
||||||
|
</span>
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<DropdownMenuItem disabled>No document types</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
)}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<Drawer
|
||||||
|
open={filterDrawerOpen}
|
||||||
|
onOpenChange={setFilterDrawerOpen}
|
||||||
|
shouldScaleBackground={false}
|
||||||
|
>
|
||||||
|
<DrawerContent
|
||||||
|
className="z-80 max-h-[75vh] rounded-t-2xl border bg-popover text-popover-foreground"
|
||||||
|
overlayClassName="z-80"
|
||||||
>
|
>
|
||||||
<SlidersVertical className="h-3.5 w-3.5" />
|
<DrawerHandle className="mt-3 h-1.5 w-10" />
|
||||||
{activeTypes.length > 0 ? (
|
<DrawerTitle className="px-4 pb-2 pt-3 text-center text-base font-semibold">
|
||||||
<span className="absolute right-0.5 top-0.5 h-1.5 w-1.5 rounded-full bg-primary" />
|
|
||||||
) : null}
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end" className="w-44">
|
|
||||||
<DropdownMenuItem onSelect={onCreateFolder}>
|
|
||||||
<FolderPlus className="h-4 w-4" />
|
|
||||||
New folder
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSub>
|
|
||||||
<DropdownMenuSubTrigger>
|
|
||||||
<ListFilter className="h-4 w-4" />
|
|
||||||
Filter by type
|
Filter by type
|
||||||
</DropdownMenuSubTrigger>
|
</DrawerTitle>
|
||||||
<DropdownMenuSubContent className="w-52 max-h-72 overflow-y-auto">
|
<div className="px-4 pb-6 pt-1">
|
||||||
{documentTypes.length > 0 ? (
|
{documentTypes.length > 0 ? (
|
||||||
documentTypes.map((type) => (
|
documentTypes.map((type, index) => {
|
||||||
<DropdownMenuCheckboxItem
|
const isActive = activeTypes.includes(type);
|
||||||
key={type}
|
return (
|
||||||
checked={activeTypes.includes(type)}
|
<div key={type}>
|
||||||
onCheckedChange={(checked) => onToggleType(type, checked === true)}
|
{index > 0 && <div className="mx-3 h-px bg-popover-border" />}
|
||||||
onSelect={(event) => event.preventDefault()}
|
<button
|
||||||
>
|
type="button"
|
||||||
{getDocumentTypeIcon(type, "h-4 w-4")}
|
className="flex h-12 w-full items-center gap-3 rounded-lg px-3 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||||
<span className="min-w-0 flex-1 truncate">{getDocumentTypeLabel(type)}</span>
|
onClick={() => onToggleType(type, !isActive)}
|
||||||
<span className="ml-auto text-xs text-muted-foreground">
|
>
|
||||||
{typeCounts[type] ?? 0}
|
{getDocumentTypeIcon(type, "h-4 w-4")}
|
||||||
</span>
|
<span className="min-w-0 flex-1 truncate">{getDocumentTypeLabel(type)}</span>
|
||||||
</DropdownMenuCheckboxItem>
|
<span className="text-xs text-muted-foreground">{typeCounts[type] ?? 0}</span>
|
||||||
))
|
{isActive && <Check className="h-4 w-4 shrink-0 text-primary" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
) : (
|
) : (
|
||||||
<DropdownMenuItem disabled>No document types</DropdownMenuItem>
|
<p className="px-3 py-4 text-sm text-muted-foreground">No document types</p>
|
||||||
)}
|
)}
|
||||||
</DropdownMenuSubContent>
|
</div>
|
||||||
</DropdownMenuSub>
|
</DrawerContent>
|
||||||
</DropdownMenuContent>
|
</Drawer>
|
||||||
</DropdownMenu>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ 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";
|
||||||
import { type ReactNode, useMemo, useState } from "react";
|
import { type ReactNode, useMemo, useState } from "react";
|
||||||
|
import { ConnectAgentDialog } from "@/components/mcp/connect-agent-dialog";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Progress } from "@/components/ui/progress";
|
import { Progress } from "@/components/ui/progress";
|
||||||
|
|
@ -322,10 +323,16 @@ export function Sidebar({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!isCollapsed && (
|
||||||
|
<div className="shrink-0 py-1.5">
|
||||||
|
<ConnectAgentDialog className="w-[calc(100%-1rem)]" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<SidebarUsageFooter
|
<SidebarUsageFooter
|
||||||
pageUsage={pageUsage}
|
pageUsage={pageUsage}
|
||||||
isCollapsed={isCollapsed}
|
isCollapsed={isCollapsed}
|
||||||
hasNavSectionAbove={footerNavItems.length > 0}
|
hasNavSectionAbove={footerNavItems.length > 0 || !isCollapsed}
|
||||||
onNavigate={onNavigate}
|
onNavigate={onNavigate}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue