diff --git a/src/Components/DragDropFileInput/DragDropFileInput.css b/src/Components/DragDropFileInput/DragDropFileInput.css index e87b8ce5..81f18e1d 100644 --- a/src/Components/DragDropFileInput/DragDropFileInput.css +++ b/src/Components/DragDropFileInput/DragDropFileInput.css @@ -2,8 +2,8 @@ display: flex; justify-content: center; align-items: center; - width: 300px; /* Set your desired width here */ - height: 200px; /* Set your desired height here */ + width: 100%; + height: 400px; background-color: #fff; /* White background color */ border: 2px solid #ccc; /* Gray border color */ border-radius: 5px; /* Rounded corners */ diff --git a/src/Components/DragDropFileInput/DragDropFileInput.tsx b/src/Components/DragDropFileInput/DragDropFileInput.tsx index ab7e03ad..ef462fc3 100644 --- a/src/Components/DragDropFileInput/DragDropFileInput.tsx +++ b/src/Components/DragDropFileInput/DragDropFileInput.tsx @@ -1,14 +1,19 @@ -import React, { useState, useRef } from "react"; +import React, { useState, useRef, useEffect } from "react"; import "./DragDropFileInput.css"; interface FileInputProps { sendChange: (files: File[]) => void; + file: string; } -const DragDropFileInput: React.FC = ({ sendChange }) => { +const DragDropFileInput: React.FC = ({ sendChange, file }) => { const [dragActive, setDragActive] = useState(false); - const fileInput = useRef(null); - const [file, setFile] = useState(""); - + const fileInput = useRef(null); + useEffect(()=>{ + if(file==""){ + const input = fileInput!.current as HTMLInputElement; + input.value=""; + } + },[file]) const handleDrag = (event: React.DragEvent) => { event.preventDefault(); if (event.type === "dragover") { @@ -35,12 +40,6 @@ const DragDropFileInput: React.FC = ({ sendChange }) => { const handleFileChange = (files: File[]) => { if (files!.length > 0) { - const reader = new FileReader(); - reader.onload = (e) => { - const newFile = e!.target!.result! as string; - setFile(newFile); - }; - reader.readAsDataURL(files[0]!); sendChange(files); } }; @@ -51,7 +50,7 @@ const DragDropFileInput: React.FC = ({ sendChange }) => { }; const handleCancel = () => { - setFile(""); + sendChange([]); }; return ( diff --git a/src/Components/FileList/FileElement/FileElement.css b/src/Components/FileList/FileElement/FileElement.css index 7d79e7e3..b64b4935 100644 --- a/src/Components/FileList/FileElement/FileElement.css +++ b/src/Components/FileList/FileElement/FileElement.css @@ -1,3 +1,78 @@ .file-element{ - border: 2px solid #ddd; -} \ No newline at end of file + position: relative; + border: 2px solid #999; + border-radius: 5px; + background-color: #ccc; + display:grid; + grid-template-areas: "pic title"; + grid-template-columns: 36% 62%; + gap: 2%; + align-items: center; + width: 95%; + margin: 5px 2%; + transition: box-shadow 0.3s ease; + transition: border-color 0.3s ease; +} + + +.file-element:hover{ + box-shadow: 0 8px 16px rgba(255, 255, 255, 0.2); +} + +.file-element.selected{ + background-color: #aaa; + border-color: #666; +} + +.file-element img{ + max-width:100px; + margin:5px; + height: auto; + grid-area: pic; +} + +.file-element p{ + grid-area: title; + overflow: hidden; +} +.file-element:hover .cross { + opacity: 1; +} +.cross { + position: absolute; + top: -15px; + right: -15px; + width: 30px; + height: 30px; + background-color: transparent; + cursor: pointer; + opacity: 0; + transition: opacity 0.3s ease; + border: solid; + border-radius: 100%; + background: #777a; +} + + .card:hover .cross { + opacity: 1; + } + + .cross:before, + .cross:after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 2px; + height: 15px; + background-color: #333; + transform: translate(-50%, -50%); + } + + .cross:before { + transform: translate(-50%, -50%) rotate(45deg); + } + + .cross:after { + transform: translate(-50%, -50%) rotate(-45deg); + } \ No newline at end of file diff --git a/src/Components/FileList/FileElement/FileElement.tsx b/src/Components/FileList/FileElement/FileElement.tsx index 5bd20416..54db4a1f 100644 --- a/src/Components/FileList/FileElement/FileElement.tsx +++ b/src/Components/FileList/FileElement/FileElement.tsx @@ -1,25 +1,52 @@ -import React from "react"; - -interface FileElementProps{ - file: File; - position: number; +import React, { useState, useEffect, useRef } from "react"; +import "./FileElement.css" +interface FileElementProps { + key: number; + file: File; + position: number; + onClick: (selected:boolean) => void; // Function to be called on click + onDelete: ()=>void; } -const FileElement: React.FC = ({file, position})=>{ - console.log(file) - let fileUrl = ""; +const FileElement: React.FC = ({ file, position, onClick, onDelete }) => { + const [fileUrl, setFileUrl] = useState(""); + const fileCard = useRef(null); + useEffect(() => { const reader = new FileReader(); - reader.onload = (e) => { - const newFile = e!.target!.result!; - fileUrl = newFile as string - }; + reader.onload = (e) => setFileUrl(e?.target?.result as string || ""); reader.readAsDataURL(file); - return( -
- -
- ) + }, [file]); -} + const handleClick = (event: React.MouseEvent) => { + event.preventDefault(); // Prevent default navigation + const element = event.target as HTMLElement; + if(element.className !== ("cross")){ + document.querySelectorAll(".file-element").forEach(div=>(div.id!=fileCard.current?.id && div.classList.remove("selected"))) + if(fileCard.current?.classList.contains("selected")){ + onClick?.(false); + fileCard.current?.classList.remove("selected"); + }else{ + onClick?.(true); + fileCard.current?.classList.add("selected"); + } + } + }; + + const deleteImage = (event: React.MouseEvent)=>{ + event.preventDefault() + onDelete(); + } + + return ( +
+ {file.name} +

{file.name}

+
+
+ ); +}; -export default FileElement \ No newline at end of file +export default FileElement; diff --git a/src/Components/FileList/FileList.css b/src/Components/FileList/FileList.css index 1b5191f4..f7fecb6d 100644 --- a/src/Components/FileList/FileList.css +++ b/src/Components/FileList/FileList.css @@ -1,21 +1,59 @@ -.file-list{ +.file-list { border: 2px solid #ccc; border-radius: 5px; grid-area: b; padding: 5px; + width: 105%; + +} + +@media only screen and (min-width:850px) { + .file-list.empty{ + width: 100%; + } } -.file-list.empty{ +@media only screen and (max-width:850px){ + .file-list.empty{ + width: 96%; + } + .file-list{ display: flex; - align-items: center; justify-content: center; - width: 159%; + flex-direction: column; + } } -.no-element{ + + .file-list.empty { + display: flex; + align-items: center; + justify-content: center; + } + + .no-element { display: none; -} - -.no-element.active{ + } + + .no-element.active { display: block; -} \ No newline at end of file + } + + .overlay { + position: absolute; /* Positioned absolutely within FileList */ + top: 0; + left: 0; /* Initial position (can be adjusted based on selection) */ + width: 100%; /* Adjust width/height as needed */ + height: 100%; + background-color: rgba(0, 0, 0, 0.5); /* Gray overlay with opacity */ + display: flex; /* Center image content horizontally and vertically */ + align-items: center; + justify-content: center; + z-index: 1; /* Ensure overlay is above FileElement components */ + visibility: hidden; /* Initially hidden */ + } + + .overlay.active { + visibility: visible; /* Show overlay when an image is selected */ + } + \ No newline at end of file diff --git a/src/Components/FileList/FileList.tsx b/src/Components/FileList/FileList.tsx index 562bf3db..a1ae1f9c 100644 --- a/src/Components/FileList/FileList.tsx +++ b/src/Components/FileList/FileList.tsx @@ -1,24 +1,47 @@ -import React, { useState } from "react"; +import React, { useState, useRef } from "react"; import "./FileList.css"; import FileElement from "./FileElement/FileElement"; -interface FileListProps{ - files: File[] + +interface FileListProps { + files: File[]; + onSelectedChange: (file:File|null)=>void; + propagateDelete: (file:File, wasShown:boolean)=>void; } -const FileList: React.FC = ({files})=>{ +const FileList: React.FC = ({ files, onSelectedChange, propagateDelete}) => { + const [selectedFile, setSelectedFile] = useState(null); - return ( -
-
- No element to show -
- {[...files].map( - (file: File, index: number, array: File[]) => ( - - ) - )} -
- ); -} + const handleSelectFile = (file: File|null, index: number) => { + setSelectedFile(file); + onSelectedChange(file) + }; + + const handleDelete = (file: File, index:number)=>{ + if(selectedFile===file){ + setSelectedFile(null) + propagateDelete(file, false) + }else{ + propagateDelete(file, files[files.length-1]===file); + } + + } + + return ( +
+
+ No element to show +
+ {[...files].map((file: File, index: number, array: File[]) => ( + selected ? handleSelectFile(file, index): handleSelectFile(null,-1)} + onDelete={()=>handleDelete(file, index)} + /> + ))} +
+ ); +}; -export default FileList; \ No newline at end of file +export default FileList; diff --git a/src/Components/Header/Header.css b/src/Components/Header/Header.css index 18036a48..95279c87 100644 --- a/src/Components/Header/Header.css +++ b/src/Components/Header/Header.css @@ -1,4 +1,4 @@ -header { +header nav { background-color: #05486c; width: 100%; display: flex; @@ -9,7 +9,6 @@ header { #version { font-weight: 700; - position: absolute; right: 12px; color: red; align-content: center; @@ -18,6 +17,30 @@ header { border-radius: 5px; } + +#header-img{ + max-height: 50px; + width: 90%; +} + +@media only screen and (min-width:850px) { + #version{ + position: absolute; + } +} + +@media only screen and (max-width:850px){ + #version{ + width: 90%; + position: initial; + } + header ul{ + padding:0; + flex-direction: column; + align-items: center; + } +} + ul { list-style-type: none; display: flex; diff --git a/src/Components/Header/Header.tsx b/src/Components/Header/Header.tsx index 9786f1db..a7aca6c1 100644 --- a/src/Components/Header/Header.tsx +++ b/src/Components/Header/Header.tsx @@ -13,9 +13,9 @@ function Header() { CFIA logo diff --git a/src/Pages/HomePage/HomePage.css b/src/Pages/HomePage/HomePage.css index 3d24302c..4e9cb87c 100644 --- a/src/Pages/HomePage/HomePage.css +++ b/src/Pages/HomePage/HomePage.css @@ -2,14 +2,31 @@ display: grid; grid-template-rows: auto; grid-template-columns: auto; - grid-template-areas: ". a b"; gap:10px; - justify-content: space-evenly; - padding: 0% 3%; + +} + +@media only screen and (min-width:850px) { + .container{ + grid-template-areas: + "a b" + "c b"; + grid-template-columns: 52% 33%; + justify-content: space-evenly; + padding: 0% 3%; + } +} + +@media only screen and (max-width:850px){ + .container{ + grid-template-areas: "a" + "c" + "b"; + padding: 0% 10%; + } } .submit-btn{ margin-top:50px; - position:relative; - width: 30%; + grid-area: c; } \ No newline at end of file diff --git a/src/Pages/HomePage/HomePage.tsx b/src/Pages/HomePage/HomePage.tsx index 02208ed1..240cdeeb 100644 --- a/src/Pages/HomePage/HomePage.tsx +++ b/src/Pages/HomePage/HomePage.tsx @@ -51,6 +51,13 @@ const [form, setForm] = useState({ all_other_text_en_1: "", all_other_text_en_2: "", }); + const [toShow, setShow] = useState("") + + const reader = new FileReader(); + reader.onload = (e) => { + const newFile = e!.target!.result! as string; + setShow(newFile); + }; const handleChange = (e: ChangeEvent) => { @@ -61,21 +68,40 @@ const [form, setForm] = useState({ const handlePhotoChange = (newFiles: File[]) => { if (newFiles!.length > 0) { setFiles([...files, ...newFiles]); + reader.readAsDataURL(newFiles[0]!); + }else{ + setShow(""); } + }; + const handleSelectedChange = (selection: File|null)=>{ + if(selection){ + reader.readAsDataURL(selection); + }else{ + setShow(""); + } + } + const navigate = useNavigate(); const Submit = ()=>{ - navigate('/Json',{state:{data:files}}) + navigate('/Json',{state:{data:files}}); + } + + const handleDeletion = (toDelete:File, wasShown:boolean)=>{ + setFiles(files.filter(file=>file!==toDelete)) + if(wasShown){ + setShow("") + } } return (
- - {/**/} + + +
-
); } diff --git a/src/Pages/JsonPage/Json.tsx b/src/Pages/JsonPage/Json.tsx index 863db864..c202ffeb 100644 --- a/src/Pages/JsonPage/Json.tsx +++ b/src/Pages/JsonPage/Json.tsx @@ -8,38 +8,45 @@ function JsonPage(){ const [form, setForm]=useState({}) const [fetchError, setError]=useState(null) const location = useLocation(); - const file:File = location.state.data[0]; + const files:File[] = location.state.data[0]; + const [uploadStarted, startUpload] = useState(false); - console.log(file) const api_url = "http://localhost:5000" - useEffect(()=>{ - const formData = new FormData() - formData.append("file",file) - fetch(api_url+"/upload",{ - method:'POST', - headers:{ - - }, - body:formData - }).then((res:Response)=>{ - fetch(api_url+"/analyze",{ - method:'GET', + const upload_all = async ()=>{ + for(let i=0; i{ - response.json().then((data)=>{ - console.log(data) - setForm(data) - setLoading(false) - }).catch(e=>{ - setLoading(false) - setError(e) - console.log(e) + // if needed, add headers here + }, + body:formData + }) + } + } + useEffect(()=>{ + if(!uploadStarted){ + startUpload(true); + upload_all().then(()=>{ + fetch(api_url+"/analyze",{ + method:'GET', + headers:{ + + } + }).then((response:Response)=>{ + response.json().then((data)=>{ + setForm(data) + setLoading(false) + }).catch(e=>{ + setLoading(false) + setError(e) + console.log(e) + }) }) }) - }) + } },[]) return (
diff --git a/src/index.css b/src/index.css index 6dc92971..aef1e4c3 100644 --- a/src/index.css +++ b/src/index.css @@ -65,3 +65,21 @@ button:focus-visible { background-color: #f9f9f9; } } + + +.black{ + color: #1a1a1a; +} + +.bold{ + font-weight: 700; +} + +.unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} \ No newline at end of file