"use client" import { useCallback, useEffect, useMemo, useState } from "react" import Link from "next/link" import { Button } from "@/components/ui/button" import { ArrowLeft, Loader2 } from "lucide-react" import { OfficeEditorChrome } from "@/components/drive/office-editor-chrome" import { RichTextDocumentEditor } from "@/components/drive/richtext-document" import { displayFileBaseName } from "@/lib/drive/display-file-name" 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" import { getGuestEditorIdentity } from "@/lib/drive/guest-editor-identity" import type { RichTextSaveStatus, RichTextSessionResponse } from "@/lib/drive/richtext-types" import { fetchPublicShareBlob } from "@/lib/api/public-share" import { cn } from "@/lib/utils" function fileNameFromPath(filePath: string, fallback?: string): string { const base = filePath.split("/").filter(Boolean).pop() return base || fallback || filePath } function saveStatusLabel(status: RichTextSaveStatus): string { switch (status) { case "saving": return "Enregistrement…" case "saved": return "Enregistré" case "error": return "Erreur d'enregistrement" default: return "" } } export function PublicRichTextEditor({ 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 guest = useMemo(() => getGuestEditorIdentity(token), [token]) const [session, setSession] = useState(null) const [error, setError] = useState(null) const [saveStatus, setSaveStatus] = useState("idle") 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 setSession(null) setError(null) void (async () => { try { const res = await fetch( `/api/v1/drive/public/shares/${encodeURIComponent(token)}/richtext/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 indisponible") const data = (await res.json()) as RichTextSessionResponse if (!cancelled) { setSession(data) setResolvedMode(data.mode === "view" ? "view" : mode) } } catch (e) { if (!cancelled) setError(e instanceof Error ? e.message : "Impossible d'ouvrir le document") } })() return () => { cancelled = true } }, [token, filePath, mode, password, guest.guestId, guest.guestName, fileName]) const fetchSourceBytes = useCallback( async (path: string) => { const blob = await fetchPublicShareBlob( token, { path, name: path.split("/").pop() ?? path }, password ) return blob.arrayBuffer() }, [token, password] ) const importApi = useCallback( async (body: { source_path: string; content: Record }) => { const res = await fetch( `/api/v1/drive/public/shares/${encodeURIComponent(token)}/richtext/import`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...body, password: password ?? "", display_name: fileName }), } ) if (!res.ok) throw new Error("Import impossible") }, [token, password, fileName] ) const statusText = saveStatusLabel(saveStatus) if (error) { return (

{error}

{showBack ? ( ) : null}
) } return (
Vous êtes {guest.guestName} {resolvedMode === "view" ? ( Lecture seule ) : null} {statusText ? ( {statusText} ) : null}
} />
{!session ? (
) : ( )}
) }