ultisuite-client/components/drive/drive-sidebar.tsx
R3D347HR4Y 6ec95262af Add OnlyOffice integration and update project configurations
- Updated .env.example to include configuration for OnlyOffice Document Server.
- Modified the workspace configuration to remove the drive-suite path.
- Adjusted TypeScript environment imports for consistency.
- Enhanced Next.js configuration to disable canvas in Webpack.
- Updated package.json to include new dependencies for OnlyOffice and PDF.js.
- Added global styles for OnlyOffice theme integration in the CSS.
- Created new layout and page components for the Drive feature, including public sharing and editing functionalities.
- Updated metadata handling across various layouts to reflect the new app structure.
2026-06-07 15:49:21 +02:00

174 lines
6.1 KiB
TypeScript

"use client"
import { useMemo, useState } from "react"
import Link from "next/link"
import { useParams, usePathname } from "next/navigation"
import { Icon } from "@iconify/react"
import { Clock, Star, Trash2 } from "lucide-react"
import { cn } from "@/lib/utils"
import { mailNavRowClass } from "@/lib/mail-chrome-classes"
import { DriveQuotaBar } from "@/components/drive/quota-bar"
import { DriveNewMenu } from "@/components/drive/new-menu"
import { DriveSidebarFolderTree } from "@/components/drive/sidebar-folder-tree"
import { AccountAvatar } from "@/components/suite/account-avatar"
import { AccountSwitcherSheet } from "@/components/suite/account-switcher-sheet"
import { Button } from "@/components/ui/button"
import { useIsXs } from "@/hooks/use-xs"
import { folderPathFromSegments, parseDriveSegments } from "@/lib/drive/drive-url"
import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity"
import { useDriveUIStore } from "@/lib/stores/drive-ui-store"
const OTHER_NAV = [
{ href: "/drive/recent", label: "Récents", icon: Clock },
{ href: "/drive/starred", label: "Favoris", icon: Star },
{ href: "/drive/trash", label: "Corbeille", icon: Trash2 },
]
export function DriveSidebar({
overlay = false,
open = true,
}: {
overlay?: boolean
open?: boolean
}) {
const pathname = usePathname()
const params = useParams()
const isXs = useIsXs()
const identity = useChromeIdentity()
const [accountMenuOpen, setAccountMenuOpen] = useState(false)
const setSidebarCollapsed = useDriveUIStore((s) => s.setSidebarCollapsed)
const route = useMemo(
() => parseDriveSegments(params.segments as string[] | undefined),
[params.segments]
)
const parentPath = folderPathFromSegments(route.pathSegments)
const filesSegments = route.view === "files" ? route.pathSegments : []
const sharedSegments = route.view === "shared" ? route.pathSegments : []
const closeSidebar = () => setSidebarCollapsed(true)
const displayName = identity?.name ?? "Utilisateur"
return (
<aside
className={cn(
"flex h-full w-56 shrink-0 flex-col bg-app-canvas text-foreground",
overlay
? cn(
"fixed inset-y-0 left-0 z-50 shadow-xl transition-transform duration-200 ease-linear",
open ? "translate-x-0" : "-translate-x-full pointer-events-none"
)
: "relative"
)}
aria-hidden={overlay && !open}
>
<div className="flex shrink-0 items-center justify-between gap-2 px-4 py-4">
<div className="flex min-w-0 items-center gap-2">
<img
src="/drive/ultidrive-mark.svg"
alt=""
className="h-8 w-8 shrink-0"
onError={(e) => {
;(e.target as HTMLImageElement).style.display = "none"
}}
/>
<span className="truncate text-lg font-medium">UltiDrive</span>
</div>
{isXs ? (
<Button
variant="ghost"
size="icon"
className="size-9 shrink-0 rounded-full text-gray-600 dark:text-muted-foreground"
aria-label="Réglages"
asChild
>
<Link href="/mail/settings">
<Icon icon="mdi:cog" className="size-5 shrink-0" aria-hidden />
</Link>
</Button>
) : null}
</div>
<div className="flex shrink-0 px-3 pb-3">
<DriveNewMenu parentPath={parentPath} />
</div>
<nav className="flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-2">
<div className="pb-1">
<DriveSidebarFolderTree
view="files"
pathSegments={filesSegments}
active={route.view === "files"}
/>
<DriveSidebarFolderTree
view="shared"
pathSegments={sharedSegments}
active={route.view === "shared"}
/>
</div>
{OTHER_NAV.map(({ href, label, icon: Icon }) => {
const active = pathname.startsWith(href)
return (
<Link
key={href}
href={href}
onClick={() => {
if (overlay) closeSidebar()
}}
className={cn(
"flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 text-sm",
mailNavRowClass({ isSelected: active })
)}
>
<Icon className="h-4 w-4 shrink-0" />
{label}
</Link>
)
})}
</nav>
<div
className={cn(
"sticky bottom-0 shrink-0 border-t border-border bg-app-canvas",
isXs && "pb-[calc(4rem+env(safe-area-inset-bottom))]",
)}
>
<div className={cn(isXs ? "px-3 pt-1.5 pb-0" : "p-3")}>
<DriveQuotaBar />
</div>
{isXs ? (
<>
<button
type="button"
className="flex w-full min-w-0 items-center gap-2.5 px-4 py-0.5 text-left transition-colors hover:bg-mail-nav-hover"
aria-label={`Compte : ${identity?.email ?? displayName}`}
aria-expanded={accountMenuOpen}
aria-haspopup="dialog"
onClick={() => setAccountMenuOpen(true)}
>
{identity ? (
<AccountAvatar
account={{ name: identity.name, email: identity.email }}
size="sm"
/>
) : (
<span className="flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-medium text-muted-foreground">
?
</span>
)}
<span className="min-w-0 flex-1">
<span className="block truncate text-xs text-muted-foreground">
Connecté en tant que
</span>
<span className="block truncate text-sm font-medium">
{displayName}
</span>
</span>
</button>
<AccountSwitcherSheet
open={accountMenuOpen}
onOpenChange={setAccountMenuOpen}
/>
</>
) : null}
</div>
</aside>
)
}