diff --git a/CHANGELOG.md b/CHANGELOG.md
index f6220a44..2e355e59 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,10 @@ and this project adheres to
- 🔥(backend) drop deprecated numchild columns from item
+### Added
+
+- ✨(frontend) add CTA on public link for anonymous and authenticated users
+
## [v0.18.0] - 2026-05-04
### Added
diff --git a/src/frontend/apps/drive/src/features/ui/components/user/UserProfile.tsx b/src/frontend/apps/drive/src/features/ui/components/user/UserProfile.tsx
index c7af44fd..bb2361f2 100644
--- a/src/frontend/apps/drive/src/features/ui/components/user/UserProfile.tsx
+++ b/src/frontend/apps/drive/src/features/ui/components/user/UserProfile.tsx
@@ -1,8 +1,20 @@
-import { UserMenu } from "@gouvfr-lasuite/ui-kit";
+import {
+ DropdownMenu,
+ Icon,
+ IconSize,
+ useDropdownMenu,
+ UserMenu,
+} from "@gouvfr-lasuite/ui-kit";
import { useAuth } from "@/features/auth/Auth";
import { logout } from "@/features/auth/Auth";
-import { LanguagePickerUserMenu } from "@/features/layouts/components/header/Header";
-import { LoginButton } from "@/features/auth/components/LoginButton";
+import {
+ LanguagePickerUserMenu,
+ LANGUAGES,
+} from "@/features/layouts/components/header/Header";
+import { AnonymousCTA } from "../anonymous-cta/AnonymousCTA";
+import { Button } from "@gouvfr-lasuite/cunningham-react";
+import { useTranslation } from "react-i18next";
+import { useClipboard } from "@/hooks/useCopyToClipboard";
export const UserProfile = () => {
const { user } = useAuth();
@@ -16,8 +28,50 @@ export const UserProfile = () => {
actions={}
/>
) : (
-
+ <>
+
+
+ >
)}
>
);
};
+
+const AnonymousDropdownMenu = () => {
+ const { isOpen, setIsOpen } = useDropdownMenu();
+ const { t, i18n } = useTranslation();
+ const copyToClipboard = useClipboard();
+
+ return (
+ ,
+ label: t("anonymous_dropdown_menu.copy_link"),
+ callback: () => {
+ copyToClipboard(window.location.href);
+ },
+ },
+ {
+ icon: ,
+ label: t("anonymous_dropdown_menu.languages"),
+ children: LANGUAGES.map((language) => ({
+ label: language.label,
+ callback: () => {
+ i18n.changeLanguage(language.value);
+ },
+ })),
+ },
+ ]}
+ >
+ }
+ variant="tertiary"
+ onClick={() => setIsOpen(!isOpen)}
+ data-testid="anonymous-dropdown-menu"
+ />
+
+ );
+};
diff --git a/src/frontend/apps/drive/src/features/ui/preview/CustomFilesPreview.tsx b/src/frontend/apps/drive/src/features/ui/preview/CustomFilesPreview.tsx
index 70caec49..b9a43913 100644
--- a/src/frontend/apps/drive/src/features/ui/preview/CustomFilesPreview.tsx
+++ b/src/frontend/apps/drive/src/features/ui/preview/CustomFilesPreview.tsx
@@ -1,7 +1,7 @@
import { Item, ItemType } from "@/features/drivers/types";
import { FilePreview, FilePreviewType } from "@gouvfr-lasuite/ui-kit";
import { useTranslation } from "react-i18next";
-import { useMemo } from "react";
+import { PropsWithChildren, useMemo } from "react";
import posthog from "posthog-js";
import { itemToPreviewFile } from "@/features/explorer/utils/utils";
import { useDownloadItem } from "@/features/items/hooks/useDownloadItem";
@@ -9,17 +9,29 @@ import { ItemInfo } from "@/features/items/components/ItemInfo";
import { Button, useModal } from "@gouvfr-lasuite/cunningham-react";
import { ItemShareModal } from "@/features/explorer/components/modals/share/ItemShareModal";
import { openWopiInNewTab } from "@/features/wopi/openWopi";
+import { useAuth } from "@/features/auth/Auth";
+import { AnonymousCTA } from "../components/anonymous-cta/AnonymousCTA";
+import { MyFilesCTA } from "../components/my-files-cta/MyFilesCTA";
+
+export enum CustomFilesPreviewMode {
+ // The actions header will be the default actions header.
+ DEFAULT = "default",
+ // The actions header will be contextual to the authentication status of the user.
+ CONTEXTUAL = "contextual",
+}
type CustomFilesPreviewProps = {
currentItem?: Item;
items: Item[];
setPreviewItem?: (item?: Item) => void;
+ mode?: CustomFilesPreviewMode;
};
export const CustomFilesPreview = ({
currentItem,
items,
setPreviewItem,
+ mode = CustomFilesPreviewMode.DEFAULT,
}: CustomFilesPreviewProps) => {
const { handleDownloadItem } = useDownloadItem();
@@ -54,9 +66,11 @@ export const CustomFilesPreview = ({
})
}
onOpenInEditor={openWopiInNewTab}
- headerRightContent={
-
- }
+ customHeaderActions={(actions) => (
+
+ {actions}
+
+ )}
sidebarContent={currentItem && }
/>
);
@@ -64,12 +78,16 @@ export const CustomFilesPreview = ({
type CustomFilesPreviewRightHeaderProps = {
currentItem?: Item;
-};
+ mode: CustomFilesPreviewMode;
+} & PropsWithChildren;
const CustomFilesPreviewRightHeader = ({
+ children,
currentItem,
+ mode,
}: CustomFilesPreviewRightHeaderProps) => {
const { t } = useTranslation();
+ const { user } = useAuth();
const shareModal = useModal();
if (!currentItem) {
@@ -78,14 +96,26 @@ const CustomFilesPreviewRightHeader = ({
return (
<>
-
-
-
+ {mode === CustomFilesPreviewMode.DEFAULT && (
+ <>
+
+
+
- {shareModal.isOpen && (
-
+ {shareModal.isOpen && (
+
+ )}
+ >
+ )}
+
+ {children}
+
+ {mode === CustomFilesPreviewMode.CONTEXTUAL && (
+
)}
>
);
diff --git a/src/frontend/apps/drive/src/pages/explorer/items/files/[id].tsx b/src/frontend/apps/drive/src/pages/explorer/items/files/[id].tsx
index 1f46c65a..3b7cab05 100644
--- a/src/frontend/apps/drive/src/pages/explorer/items/files/[id].tsx
+++ b/src/frontend/apps/drive/src/pages/explorer/items/files/[id].tsx
@@ -1,11 +1,15 @@
import { GenericDisclaimer } from "@/features/ui/components/generic-disclaimer/GenericDisclaimer";
import { SpinnerPage } from "@/features/ui/components/spinner/SpinnerPage";
-import { CustomFilesPreview } from "@/features/ui/preview/CustomFilesPreview";
+import {
+ CustomFilesPreview,
+ CustomFilesPreviewMode,
+} from "@/features/ui/preview/CustomFilesPreview";
import { Icon } from "@gouvfr-lasuite/ui-kit";
import { Button } from "@gouvfr-lasuite/cunningham-react";
import { useRouter } from "next/router";
import { useTranslation } from "react-i18next";
import { useItem } from "@/features/explorer/hooks/useQueries";
+import { GlobalLayout } from "@/features/layouts/components/global/GlobalLayout";
export default function FilePage() {
const { t } = useTranslation();
@@ -37,7 +41,15 @@ export default function FilePage() {
return (
-
+
);
}
+
+FilePage.getLayout = function getLayout(page: React.ReactElement) {
+ return {page};
+};