ultisuite-client/components/drive/public-office-editor.tsx
R3D347HR4Y 8f81d7aba1
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(admin-settings): refactor admin settings components for improved usability and consistency
- Replaced legacy components with new `SettingsCard`, `SettingsField`, and `SettingsToggleRow` for a unified design.
- Enhanced `AdminListControls` to support compact mode and improved pagination controls.
- Updated various sections including `AiAssistantSection`, `AuthenticationSection`, and `DriveMountOAuthSection` to utilize new components, streamlining the settings interface.
- Improved accessibility and user experience across admin settings with clearer labels and hints.
- Deprecated old components while maintaining backward compatibility for existing admin sections.
2026-06-15 11:10:17 +02:00

160 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { ArrowLeft } from "lucide-react"
import { OnlyOfficeMount } from "@/components/drive/onlyoffice-mount"
import { OfficeEditorChrome } from "@/components/drive/office-editor-chrome"
import { displayFileBaseName } from "@/lib/drive/display-file-name"
import { getGuestEditorIdentity } from "@/lib/drive/guest-editor-identity"
import { resolvePublicShareEditReturnTo, shouldShowPublicShareEditorBack } from "@/lib/drive/public-share-url"
import type { PublicShareRootType } from "@/lib/drive/public-share-url"
import { useDriveDocumentTitle } from "@/lib/drive/use-drive-document-title"
function fileNameFromPath(filePath: string, fallback?: string): string {
const base = filePath.split("/").filter(Boolean).pop()
return base || fallback || filePath
}
export function PublicOfficeEditor({
token,
filePath,
password,
returnTo,
mode = "edit",
fileDisplayName,
shareRoot,
}: {
token: string
filePath: string
password?: string
returnTo?: string | null
mode?: "edit" | "view"
fileDisplayName?: string
shareRoot?: PublicShareRootType | null
}) {
const instanceSeq = useRef(0)
const guest = useMemo(() => getGuestEditorIdentity(token), [token])
const [config, setConfig] = useState<Record<string, unknown> | null>(null)
const [serverUrl, setServerUrl] = useState("")
const [editorId, setEditorId] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
const [resolvedMode, setResolvedMode] = useState<"edit" | "view">(mode)
const fileName = fileDisplayName || fileNameFromPath(filePath)
const title = displayFileBaseName(fileName)
useDriveDocumentTitle(title)
const backHref = useMemo(
() => resolvePublicShareEditReturnTo(token, returnTo, filePath),
[token, returnTo, filePath]
)
const showBack = shouldShowPublicShareEditorBack(shareRoot, returnTo, filePath)
useEffect(() => {
let cancelled = false
setConfig(null)
setServerUrl("")
setEditorId(null)
setError(null)
setResolvedMode(mode)
void (async () => {
try {
const res = await fetch(
`/api/v1/drive/public/shares/${encodeURIComponent(token)}/office/session`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
path: filePath,
mode,
password: password ?? "",
guest_id: guest.guestId,
guest_name: guest.guestName,
display_name: fileName,
}),
}
)
if (!res.ok) throw new Error("session_failed")
const data = (await res.json()) as {
config: Record<string, unknown>
serverUrl: string
mode?: "edit" | "view"
}
if (cancelled) return
instanceSeq.current += 1
setConfig(data.config)
setServerUrl(data.serverUrl || process.env.NEXT_PUBLIC_ONLYOFFICE_URL || "")
setEditorId(`ultidrive-public-editor-${instanceSeq.current}`)
if (data.mode === "edit" || data.mode === "view") {
setResolvedMode(data.mode)
} else {
const editorConfig = data.config?.editorConfig as { mode?: string } | undefined
if (editorConfig?.mode === "edit" || editorConfig?.mode === "view") {
setResolvedMode(editorConfig.mode)
}
}
} catch {
if (!cancelled) setError("Impossible de charger léditeur.")
}
})()
return () => {
cancelled = true
}
}, [token, filePath, password, mode, fileName])
const handleEditorError = useCallback((message: string) => {
setError(message)
}, [])
if (error) {
return (
<div className="flex h-dvh flex-col items-center justify-center gap-4">
<p className="text-destructive">{error}</p>
{showBack ? (
<Button asChild variant="outline">
<Link href={backHref}>
<ArrowLeft className="mr-2 h-4 w-4" />
Retour
</Link>
</Button>
) : null}
</div>
)
}
if (!config || !editorId || !serverUrl) {
return <p className="p-8 text-center text-muted-foreground">Ouverture du document</p>
}
return (
<div className="flex h-dvh flex-col">
<OfficeEditorChrome
backHref={backHref}
backLabel="Partage"
showBack={showBack}
title={title}
trailing={
resolvedMode === "view" ? (
<span className="rounded-md bg-muted px-2 py-1 text-xs text-muted-foreground">
Lecture seule
</span>
) : null
}
/>
<div className="relative min-h-0 flex-1">
<OnlyOfficeMount
editorId={editorId}
documentServerUrl={serverUrl.replace(/\/$/, "")}
config={config}
onError={handleEditorError}
scriptId="onlyoffice-docs-api-public"
/>
</div>
</div>
)
}