- 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.
155 lines
4.6 KiB
TypeScript
155 lines
4.6 KiB
TypeScript
"use client"
|
|
|
|
import { useMemo, useState } from "react"
|
|
import { useRouter } from "next/navigation"
|
|
import { useQueryClient } from "@tanstack/react-query"
|
|
import { toast } from "sonner"
|
|
import { useDriveList, useDriveMutations } from "@/lib/api/hooks/use-drive-queries"
|
|
import { uploadFile } from "@/lib/api/upload"
|
|
import { nextUntitledName } from "@/lib/drive/drive-default-name"
|
|
import { buildDriveEditHref } from "@/lib/drive/drive-url"
|
|
|
|
export type DriveNewKind = "document" | "spreadsheet" | "presentation" | "folder"
|
|
|
|
export const DRIVE_NEW_KIND_META: Record<
|
|
DriveNewKind,
|
|
{ ext: string; typeLabel: string; menuLabel: string }
|
|
> = {
|
|
document: { ext: ".docx", typeLabel: "Document", menuLabel: "Document" },
|
|
spreadsheet: { ext: ".xlsx", typeLabel: "Tableur", menuLabel: "Tableur" },
|
|
presentation: { ext: ".pptx", typeLabel: "Présentation", menuLabel: "Présentation" },
|
|
folder: { ext: "", typeLabel: "Dossier", menuLabel: "Dossier" },
|
|
}
|
|
|
|
export const DRIVE_NEW_MENU_ITEM_CLASS =
|
|
"gap-3 rounded-md py-2.5 pr-3 pl-3 text-[15px] focus:bg-accent/80 [&_svg]:size-5"
|
|
|
|
async function importFolderTree(
|
|
files: FileList,
|
|
parentPath: string,
|
|
createFolder: ReturnType<typeof useDriveMutations>["createFolder"]
|
|
) {
|
|
const base = parentPath === "/" ? "" : parentPath
|
|
const created = new Set<string>()
|
|
|
|
const ensureDir = async (dirPath: string) => {
|
|
if (created.has(dirPath)) return
|
|
try {
|
|
await createFolder.mutateAsync(dirPath)
|
|
} catch {
|
|
/* dossier peut déjà exister */
|
|
}
|
|
created.add(dirPath)
|
|
}
|
|
|
|
for (const file of Array.from(files)) {
|
|
const rel = file.webkitRelativePath
|
|
if (!rel) continue
|
|
const segments = rel.split("/")
|
|
const name = segments.pop()
|
|
if (!name) continue
|
|
let dirAcc = base
|
|
for (const segment of segments) {
|
|
dirAcc = `${dirAcc}/${segment}`
|
|
await ensureDir(dirAcc)
|
|
}
|
|
await uploadFile(`${dirAcc}/${name}`, file)
|
|
}
|
|
}
|
|
|
|
export function useDriveNewMenu(parentPath: string) {
|
|
const router = useRouter()
|
|
const queryClient = useQueryClient()
|
|
const { createFolder, createFile } = useDriveMutations()
|
|
const base = parentPath === "/" ? "" : parentPath
|
|
const list = useDriveList(parentPath)
|
|
const siblingNames = useMemo(
|
|
() => (list.data?.files ?? []).map((f) => f.name),
|
|
[list.data?.files]
|
|
)
|
|
|
|
const [pendingKind, setPendingKind] = useState<DriveNewKind | null>(null)
|
|
|
|
const refresh = () => queryClient.invalidateQueries({ queryKey: ["drive"] })
|
|
|
|
const pendingMeta = pendingKind ? DRIVE_NEW_KIND_META[pendingKind] : null
|
|
const defaultName =
|
|
pendingMeta && pendingKind
|
|
? nextUntitledName(siblingNames, pendingMeta.typeLabel, pendingMeta.ext)
|
|
: ""
|
|
|
|
const confirmNew = async (rawName: string) => {
|
|
if (!pendingKind || !pendingMeta) return
|
|
const name = rawName.trim().replace(/\//g, "")
|
|
if (!name) return
|
|
|
|
if (pendingKind === "folder") {
|
|
try {
|
|
await createFolder.mutateAsync(`${base}/${name}`)
|
|
toast.success("Dossier créé")
|
|
await refresh()
|
|
} catch {
|
|
toast.error("Impossible de créer le dossier")
|
|
throw new Error("folder create failed")
|
|
}
|
|
return
|
|
}
|
|
|
|
const fileName = name.endsWith(pendingMeta.ext) ? name : name + pendingMeta.ext
|
|
try {
|
|
const { path } = await createFile.mutateAsync({
|
|
parent_path: parentPath,
|
|
name: fileName,
|
|
kind: pendingKind,
|
|
})
|
|
toast.success("Fichier créé")
|
|
const returnTo =
|
|
typeof window !== "undefined"
|
|
? window.location.pathname + window.location.search
|
|
: undefined
|
|
router.push(buildDriveEditHref(path, returnTo))
|
|
} catch {
|
|
toast.error("Impossible de créer le fichier")
|
|
throw new Error("file create failed")
|
|
}
|
|
}
|
|
|
|
const uploadFiles = async (files: FileList | null) => {
|
|
if (!files?.length) return
|
|
for (const file of Array.from(files)) {
|
|
try {
|
|
await uploadFile(`${base}/${file.name}`, file)
|
|
} catch {
|
|
toast.error(`Échec : ${file.name}`)
|
|
}
|
|
}
|
|
await refresh()
|
|
toast.success("Import terminé")
|
|
}
|
|
|
|
const importFolder = async (files: FileList | null) => {
|
|
if (!files?.length) return
|
|
try {
|
|
await importFolderTree(files, parentPath, createFolder)
|
|
await refresh()
|
|
toast.success("Dossier importé")
|
|
} catch {
|
|
toast.error("Impossible d'importer le dossier")
|
|
}
|
|
}
|
|
|
|
const pickKind = (kind: DriveNewKind) => setPendingKind(kind)
|
|
const closeNameDialog = () => setPendingKind(null)
|
|
|
|
return {
|
|
pendingKind,
|
|
pendingMeta,
|
|
defaultName,
|
|
confirmNew,
|
|
uploadFiles,
|
|
importFolder,
|
|
pickKind,
|
|
closeNameDialog,
|
|
}
|
|
}
|