# HG changeset patch
# User Arnaud Vergnet <arnaud.vergnet@logilab.fr>
# Date 1712582309 -7200
#      Mon Apr 08 15:18:29 2024 +0200
# Node ID 7f50dc6cef5f423f927a262fa69bfe03b9f72097
# Parent  a97a1747f5bbb1efcc13134c7143ec0d9bf439a9
feat(frontend): use native <a> props for file downloads

diff --git a/frontend/src/components/FileField.tsx b/frontend/src/components/FileField.tsx
--- a/frontend/src/components/FileField.tsx
+++ b/frontend/src/components/FileField.tsx
@@ -3,7 +3,7 @@
 import Visibility from "@mui/icons-material/Visibility";
 import { ChangeEventHandler, useRef } from "react";
 import { CWFile } from "@/types";
-import { downloadFile, getFileNameFromURL, openFile } from "@/utils";
+import { getFileDownloadUrl, getFileNameFromURL } from "@/utils";
 import { ButtonTooltip } from "./ButtonTooltip";
 
 interface FileFieldProps {
@@ -21,15 +21,6 @@
 }: FileFieldProps) {
   const ref = useRef<HTMLInputElement>(null);
 
-  const onPressOpen = async () => {
-    let fileToOpen = value?.data as Blob;
-    // The file is available on the server, fetch it
-    if (!fileToOpen && value?.downloadUrl) {
-      fileToOpen = await downloadFile(value.downloadUrl);
-    }
-    openFile(fileToOpen);
-  };
-
   const chooseFile = () => {
     ref.current?.click();
   };
@@ -46,10 +37,10 @@
 
   function getFileName() {
     if (value?.data) {
-      return `(${value.data.name})`;
+      return `${value.data.name}`;
     }
     if (value?.downloadUrl) {
-      return `(${getFileNameFromURL(value?.downloadUrl)})`;
+      return `${getFileNameFromURL(value?.downloadUrl)}`;
     }
     return "";
   }
@@ -73,7 +64,7 @@
         >
           {label}
           {filePresent
-            ? ` ${getFileName()}: Modifier le fichier`
+            ? ` (${getFileName()}): Modifier le fichier`
             : " : Choisir un fichier"}
         </Button>
         <ButtonTooltip
@@ -84,7 +75,8 @@
           <Button
             startIcon={<Visibility />}
             disabled={disabled || !filePresent}
-            onClick={onPressOpen}
+            href={getFileDownloadUrl(value)}
+            download={getFileName()}
           >
             Voir
           </Button>
diff --git a/frontend/src/components/ImportProcessTable.tsx b/frontend/src/components/ImportProcessTable.tsx
--- a/frontend/src/components/ImportProcessTable.tsx
+++ b/frontend/src/components/ImportProcessTable.tsx
@@ -23,9 +23,9 @@
 import { useGetImportProcesses } from "@/hooks/useGetImportProcesses";
 import { ErrorScreen } from "./ErrorScreen";
 import { LoadingScreen } from "./LoadingScreen";
-import { downloadFile, openFile } from "@/utils";
 import { ButtonTooltip } from "./ButtonTooltip";
 import { useState, useEffect } from "react";
+import { getFileNameFromURL } from "@/utils";
 
 export interface ImportProcessTableProps {
   dataServiceEid?: number;
@@ -34,15 +34,6 @@
   showProjectColumn?: boolean;
 }
 
-async function showFile(url?: string) {
-  if (url) {
-    const fileToOpen = await downloadFile(url);
-    openFile(fileToOpen);
-  } else {
-    console.error("No file to open");
-  }
-}
-
 const REFRESH_INTERVAL = 5000;
 
 export function ImportProcessTable(props: ImportProcessTableProps) {
@@ -141,7 +132,8 @@
           <IconButton
             color="primary"
             disabled={!hasLog}
-            onClick={() => showFile(importProcess.log_url)}
+            href={importProcess.log_url ?? ""}
+            download={getFileNameFromURL(importProcess.log_url)}
           >
             {hasLog ? <Visibility /> : <VisibilityOff />}
           </IconButton>
@@ -190,29 +182,55 @@
           "aria-labelledby": "dataset-menu",
         }}
       >
-        <MenuItem
-          onClick={() => {
-            showFile(importProcess.input_dataset_url);
-            handleClose();
-          }}
-          disabled={importProcess.input_dataset_url == null}
-        >
-          Entrée
-        </MenuItem>
-        <MenuItem
-          onClick={() => {
-            showFile(importProcess.output_dataset_url);
-            handleClose();
-          }}
-          disabled={importProcess.output_dataset_url == null}
-        >
-          Sortie
-        </MenuItem>
+        <DatasetMenuItem
+          text="Entrée"
+          url={importProcess.input_dataset_url}
+          onClick={handleClose}
+        />
+        <DatasetMenuItem
+          text="Sortie"
+          url={importProcess.output_dataset_url}
+          onClick={handleClose}
+        />
       </Menu>
     </TableCell>
   );
 }
 
+function DatasetMenuItem({
+  text,
+  url,
+  onClick,
+}: {
+  text: string;
+  url?: string;
+  onClick: () => void;
+}) {
+  if (!url) {
+    return (
+      <MenuItem onClick={onClick} disabled={true}>
+        {text}
+      </MenuItem>
+    );
+  }
+
+  // Putting href and download props directly on the MenuItem does not work
+  return (
+    <a
+      href={url}
+      download={getFileNameFromURL(url)}
+      style={{
+        color: "inherit",
+        textDecoration: "inherit",
+      }}
+    >
+      <MenuItem onClick={onClick} disabled={false}>
+        {text}
+      </MenuItem>
+    </a>
+  );
+}
+
 function ShaclButton({ importProcess }: { importProcess: ImportProcess }) {
   if (importProcess.shacl_valid) {
     return (
@@ -236,7 +254,8 @@
       <ButtonTooltip title={"Voir le rapport d'erreur SHACL"}>
         <IconButton
           color="primary"
-          onClick={() => showFile(importProcess.shacl_report_url)}
+          href={importProcess.shacl_report_url ?? ""}
+          download={getFileNameFromURL(importProcess.shacl_report_url)}
         >
           <Visibility color="warning" />
         </IconButton>
diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts
--- a/frontend/src/utils.ts
+++ b/frontend/src/utils.ts
@@ -1,19 +1,18 @@
-export function openFile(file?: Blob) {
-  if (file) {
-    const url = URL.createObjectURL(file);
-    window.open(url);
-  } else {
-    console.warn("No file available either locally or on the server");
+import { CWFile } from "./types";
+
+export function getFileDownloadUrl(file?: CWFile): string {
+  if (file?.data) {
+    return URL.createObjectURL(file.data);
   }
+  if (file?.downloadUrl) {
+    return file.downloadUrl;
+  }
+  return "";
 }
 
-export async function downloadFile(url: string) {
-  const response = await fetch(url, {
-    credentials: "include",
-  });
-  return await response.blob();
-}
-
-export function getFileNameFromURL(url: string): string {
+export function getFileNameFromURL(url?: string): string {
+  if (!url) {
+    return "";
+  }
   return url.slice(url.lastIndexOf("/") + 1);
 }