Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

selected download #99

Merged
merged 3 commits into from
Dec 12, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 141 additions & 89 deletions frontend/src/pages/VisitorDownloadPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import styled from "@emotion/styled";
import {Button, Card, Modal, notification, Table} from "antd";
import {DownloadOutlined} from "@ant-design/icons";
import {useSearchParams} from "react-router-dom";
import {ApolloError, useMutation} from "@apollo/client";
import {GET_FILES_FROM_UPLOAD} from "../graphql/mutations";
import {useEffect, useState} from "react";
import axios from 'axios'
import { Button, Card, Modal, notification, Table, Checkbox } from "antd";
import { DownloadOutlined } from "@ant-design/icons";
import { useSearchParams } from "react-router-dom";
import { ApolloError, useMutation } from "@apollo/client";
import { GET_FILES_FROM_UPLOAD } from "../graphql/mutations";
import { useEffect, useState } from "react";
import axios from "axios";

interface FileData {
key: string;
Expand All @@ -22,10 +22,12 @@ const VisitorDownloadPage = () => {
const [notifApi, contextHolder] = notification.useNotification();
const [searchParams] = useSearchParams();
const [files, setFiles] = useState<FileData[]>([]);
const [filePreview, setFilePreview] = useState<any>(null)
const [filePreview, setFilePreview] = useState<any>(null);
const [selectedFiles, setSelectedFiles] = useState<FileData[]>([]);
const [selectAll, setSelectAll] = useState(false);

const token = searchParams.get("token");
const [getFiles, {loading, error, data}] = useMutation(
const [getFiles, { loading, error, data }] = useMutation(
GET_FILES_FROM_UPLOAD,
{
onError: (error: ApolloError) => {
Expand All @@ -40,11 +42,12 @@ const VisitorDownloadPage = () => {

useEffect(() => {
if (token) {
getFiles({variables: {token}}).then((response) => {
getFiles({ variables: { token } }).then((response) => {
const fetchedFiles = response.data.getFilesFromUpload.map(
(file: FileData, index: number) => ({
...file,
url: `http://localhost:7002/access/download?token=${token}&fileId=${file.id}`,
key: index,
})
);
setFiles(fetchedFiles);
Expand Down Expand Up @@ -76,12 +79,16 @@ const VisitorDownloadPage = () => {
if (diffDays > 0) {
result += `${diffDays} day${diffDays > 1 ? "s" : ""}`;
if (diffHours % 24 > 0) {
result += ` and ${diffHours % 24} hour${(diffHours % 24) > 1 ? "s" : ""}`;
result += ` and ${diffHours % 24} hour${
diffHours % 24 > 1 ? "s" : ""
}`;
}
} else if (diffHours > 0) {
result += `${diffHours} hour${diffHours > 1 ? "s" : ""}`;
if (diffMinutes % 60 > 0) {
result += ` and ${diffMinutes % 60} minute${(diffMinutes % 60) > 1 ? "s" : ""}`;
result += ` and ${diffMinutes % 60} minute${
diffMinutes % 60 > 1 ? "s" : ""
}`;
}
} else if (diffMinutes > 0) {
result += `${diffMinutes} minute${diffMinutes > 1 ? "s" : ""}`;
Expand All @@ -93,26 +100,54 @@ const VisitorDownloadPage = () => {
return result + " ago";
};

const handlePreviewFile = async (fileDefaultName) => {
await axios
.post(
`http://localhost:7002/files/get-one`,
{ fileDefaultName },
{ responseType: "blob" }
)
LuckyShuii marked this conversation as resolved.
Show resolved Hide resolved
.then((res) => {
console.log(res.data);

const fileUrl = URL.createObjectURL(res.data);
setFilePreview(fileUrl);
})
.catch((err) => console.error(err));
};

const formatSizeInMB = (sizeInBytes: string) => {
const size = parseInt(sizeInBytes, 10);
if (isNaN(size)) return "N/A";
return (size / (1024 * 1024)).toFixed(2) + " MB";
};

const downloadAllFiles = async () => {
const handleSelectAll = (checked: boolean) => {
setSelectAll(checked);
setSelectedFiles(checked ? files : []);
};

const handleSelectFile = (file: FileData, checked: boolean) => {
if (checked) {
setSelectedFiles((prev) => [...prev, file]);
} else {
setSelectedFiles((prev) => prev.filter((f) => f.key !== file.key));
}
};

const downloadSelectedFiles = async () => {
try {
const response = await axios.post(
"http://localhost:7002/files/download",
{files: files.map((file) => file.default_name)},
{responseType: "blob"}
{ files: selectedFiles.map((file) => file.default_name) },
{ responseType: "blob" }
);

const blob = new Blob([response.data], {type: "application/zip"});
const blob = new Blob([response.data], { type: "application/zip" });

const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "files.zip";
link.download = "selected_files.zip";
link.click();

URL.revokeObjectURL(link.href);
Expand All @@ -121,28 +156,27 @@ const VisitorDownloadPage = () => {
}
};

const handlePreviewFile = async (fileDefaultName) => {
await axios.post(`http://localhost:7002/files/get-one`,
{fileDefaultName},
{responseType: 'blob'}
).then((res) => {
console.log(res.data)

const fileUrl = URL.createObjectURL(res.data)
setFilePreview(fileUrl)
}).catch(err => console.error(err))
}

const columns = [
{
title: (
<Checkbox
onChange={(e) => handleSelectAll(e.target.checked)}
checked={selectAll}
/>
),
dataIndex: "checkbox",
key: "checkbox",
render: (_: any, file: FileData) => (
<Checkbox
onChange={(e) => handleSelectFile(file, e.target.checked)}
checked={selectedFiles.some((f) => f.key === file.key)}
/>
),
},
{
title: "Name",
dataIndex: "name",
key: "name",
render: (text: string) => (
<span>
<FileIcon/> {text}
</span>
),
},
{
title: "Size",
Expand All @@ -160,15 +194,21 @@ const VisitorDownloadPage = () => {
title: "Action",
dataIndex: "action",
key: "action",
render: (text: string, file) => <PreviewLink
onClick={() => handlePreviewFile(file.default_name)}>preview</PreviewLink>,
render: (_: any, file: FileData) => (
<PreviewLink
onClick={() => handlePreviewFile(file.default_name)}
>
Preview
</PreviewLink>
),
},
];

return (
<VisitorDownloadWrapper>
{contextHolder}
<Card
style={{width: 800, borderRadius: "10px", padding: "25px"}}
style={{ width: 800, borderRadius: "10px", padding: "25px" }}
title={
<TitleContainer>
<TextContainer>
Expand All @@ -187,86 +227,98 @@ const VisitorDownloadPage = () => {
pagination={false}
showHeader={true}
bordered={false}
style={{marginBottom: "20px"}}
style={{ marginBottom: "20px" }}
/>
<ButtonContainer>
<Button
type="primary"
icon={<DownloadOutlined/>}
onClick={downloadAllFiles}
icon={<DownloadOutlined />}
onClick={downloadSelectedFiles}
disabled={selectedFiles.length === 0}
>
Download all
Download
</Button>
</ButtonContainer>
</Card>
<Modal open={!!filePreview} closable={true} onCancel={() => setFilePreview(null)}>
<img src={filePreview} alt='Bonjour'/>
<Modal
open={!!filePreview}
onCancel={() => setFilePreview(null)}
maskClosable={true}
centered
footer={
<Button onClick={() => setFilePreview(null)} type="primary">
Close
</Button>
}
>
<img
src={filePreview}
alt="Preview"
style={{
maxWidth: "100%",
maxHeight: "80vh",
objectFit: "contain",
display: "block",
margin: "0 auto",
}}
/>
</Modal>
</VisitorDownloadWrapper>
);
};

// Styles pour les icônes et boutons
const FileIcon = styled.span`
display: inline-block;
width: 20px;
height: 20px;
background-color: #ddd;
margin-right: 10px;
border-radius: 4px;
`;

const PreviewLink = styled.a`
color: #7f58ff;
text-decoration: underline;
`;

// Styles
const TitleContainer = styled.div`
display: flex;
justify-content: space-between;
display: flex;
justify-content: space-between;
`;

const TextContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
flex-grow: 1;
line-height: 1.4;
display: flex;
flex-direction: column;
align-items: center;
flex-grow: 1;
line-height: 1.4;

h3 {
margin: 0;
font-size: 18px;
}
h3 {
margin: 0;
font-size: 18px;
}

p {
margin: 0;
font-size: 16px;
color: #555;
}
p {
margin: 0;
font-size: 16px;
color: #555;
}
`;

const ExpirationText = styled.span`
font-size: 12px;
color: #888;
white-space: nowrap;
flex-shrink: 0;
font-size: 12px;
color: #888;
white-space: nowrap;
flex-shrink: 0;
`;

const ButtonContainer = styled.div`
display: flex;
justify-content: center;
margin-top: 20px;
display: flex;
justify-content: center;
margin-top: 20px;
`;

const VisitorDownloadWrapper = styled.div`
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 40px;
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 40px;
`;

const PreviewLink = styled.a`
color: #7f58ff;
text-decoration: underline;
`;

export default VisitorDownloadPage;
Loading