changed design. added dockerfile

This commit is contained in:
2026-05-01 01:41:34 +02:00
parent 476bb1f045
commit d1384be6f4
7 changed files with 155 additions and 79 deletions
+19
View File
@@ -0,0 +1,19 @@
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+7 -4
View File
@@ -1,13 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>cnn_website</title>
</head>
<body>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

+69 -36
View File
@@ -1,73 +1,77 @@
.custom-button {
height: 40px;
padding: 10px 20px;
background: #333;
color: rgb(225, 225, 225);
background: rgb(25, 100, 126);
color: rgb(255, 255, 255);
cursor: pointer;
border-radius: 6px;
border: 1px solid black;
border-radius: 20px;
border: 1px solid rgb(25, 100, 126);
user-select: none;
}
.custom-button:hover {
border-color: white;
color: white;
}
.custom-button.inactive {
background: #333;
color: rgb(117, 117, 117);
opacity: 0.3;
cursor: default;
}
.custom-button.inactive:hover {
border-color: black;
border-color: rgb(25, 100, 126);
}
.explanation-box a,
span,
p {
line-height: 1.7;
font-size: 17px;
#site-headline {
margin-left: 100px;
margin-bottom: 50px;
color: rgb(47, 168, 208);
font-weight: bold;
font-size: 4rem;
position: relative;
width: fit-content;
}
#site-headline::before {
content: "";
position: absolute;
bottom: 0;
right: 0;
height: 4px;
width: 100%;
border-radius: 20px;
background: rgb(47, 168, 208);
}
a {
color: rgb(0, 0, 0);
text-decoration: none;
border-bottom: 1px solid black;
border-bottom: 1px solid rgb(255, 255, 255);
padding-bottom: 2px;
transition: border-color 0.15s;
}
a:hover {
border-color: white;
border-color: #c59eff;
}
.explanation-box {
flex: 1;
min-width: 0;
min-width: 300px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
overflow: hidden;
border: 3px solid rgb(48, 62, 74);
margin: 50px;
padding: 50px;
border-radius: 12px;
background: rgb(97, 125, 151);
margin: 180px 50px 0px 50px;
}
.image-box {
border: 2px solid black;
background: rgb(69, 69, 69);
height: 400px;
width: 600px;
margin-top: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
background: rgba(0, 0, 0, 0.5);
cursor: pointer;
}
@@ -77,26 +81,55 @@ a:hover {
.image-box img {
border: 2px solid black;
border-Radius: 8px;
border-Radius: 10px;
}
.image-box:hover p {
color: white;
}
.image-box img:hover {
border-color: white;
}
.image-box.active {
background: none;
border: none;
}
.content-block {
display: flex;
align-items: center;
gap: 20px;
height: 40px;
}
/* HTML: <div class="loader"></div> */
.loader {
width: 85px;
height: 25px;
--g1: conic-gradient(from 90deg at left 3px top 3px, #0000 90deg, #fff 0);
--g2: conic-gradient(from -90deg at bottom 3px right 3px, #0000 90deg, #fff 0);
background: var(--g1), var(--g1), var(--g1), var(--g2), var(--g2), var(--g2);
background-position: left, center, right;
background-repeat: no-repeat;
animation: l8 1s infinite;
}
@keyframes l8 {
0% {
background-size: 25px 100%, 25px 100%, 25px 100%
}
20% {
background-size: 25px 50%, 25px 100%, 25px 100%
}
40% {
background-size: 25px 50%, 25px 50%, 25px 100%
}
60% {
background-size: 25px 100%, 25px 50%, 25px 50%
}
80% {
background-size: 25px 100%, 25px 100%, 25px 50%
}
100% {
background-size: 25px 100%, 25px 100%, 25px 100%
}
}
+27 -23
View File
@@ -7,26 +7,24 @@ function Bird_CNN() {
? "http://134.60.154.7:8000"
: "http://134.60.154.7:8000";
const boxRef = useRef(null);
const [file, setFile] = useState(null);
const [birdClass, setBirdClass] = useState(null);
const [confidence, setConfidence] = useState(null);
const [error, setError] = useState(false);
const [preview, setPreview] = useState(null);
const [preview, setPreview] = useState("\\cherry_bird.jpeg");
const [loading, setLoading] = useState(false);
const fileInputRef = useRef(null);
const uploadButton = useRef(null);
const scrollRef = useRef(null);
const handleImageDivClick = () => {
fileInputRef.current.click();
}
const handleImage = (e) => {
setFile(e.target.files[0]);
if (e.target.files[0]) {
setFile(e.target.files[0]);
setPreview(URL.createObjectURL(e.target.files[0]));
boxRef.current.classList.add("active");
uploadButton.current.classList.remove("inactive");
}
setConfidence(null);
@@ -36,15 +34,20 @@ function Bird_CNN() {
const sendImage = async (e) => {
if (!file) return;
scrollRef.current.scrollIntoView({ behavior: "smooth" });
const formData = new FormData();
formData.append("file", file);
try {
setLoading(true);
const response = await fetch(`${apiUrl}/predict`, {
method: "POST",
body: formData,
});
setLoading(false);
if (!response.ok) {
setError(true);
}
@@ -65,23 +68,23 @@ function Bird_CNN() {
return (
<>
<div className='site-box' style={{ marginTop: "150px" }}>
<h1 id='site-headline' style={{ marginLeft: "100px", marginBottom: "50px" }}>Bird Species Expert</h1>
<div className='content-box' style={{ display: "flex" }}>
<div className='explanation-box'>
<h2 style={{ color: "Blue" }}>Explanation</h2>
<h1 id='site-headline'>Bird Species Expert</h1>
<div className='content-box' style={{ display: "flex", flexWrap: "wrap", justifyContent: "center" }}>
<div className='explanation-box left'>
<h2 style={{ color: "rgb(47, 168, 208)" }}>Explanation</h2>
<span>Select an image and upload it to our bird expert. You will receive a classification and how certain the expert is with her opinion. Be aware that the expert may not be always right.</span>
</div>
<div className='request-box' style={{ height: "700px", width: "700px", display: "flex", flexDirection: "column", alignItems: "center", flex: "0 0 auto" }}>
<div className='input-box' style={{ width: "500px", height: "40px", display: "flex", justifyContent: "space-evenly", alignItems: "center" }}>
<div className='request-box' style={{ height: "800px", width: "700px", display: "flex", flexDirection: "column", alignItems: "center", flex: "0 0 auto", margin: "100px 50px 0px 50px" }}>
<div className='input-box' style={{ marginBottom: "80px", width: "500px", height: "40px", display: "flex", justifyContent: "space-evenly", alignItems: "center" }}>
<div>
<input ref={fileInputRef} type="file" id='fileUpload' accept="image/jpeg" onChange={handleImage} style={{ display: "none" }} />
<input disabled={loading} ref={fileInputRef} type="file" id='fileUpload' accept="image/jpeg" onChange={handleImage} style={{ display: "none" }} />
<label htmlFor="fileUpload" className="custom-button">
Select an image
Select Image
</label>
</div>
<div>
<button id='button-send' onClick={sendImage} style={{ display: "none" }} />
<button id='button-send' onClick={sendImage} style={{ display: "none" }} disabled={loading} />
<label htmlFor="button-send" className="custom-button inactive" ref={uploadButton}>
Ask Expert
</label>
@@ -89,19 +92,20 @@ function Bird_CNN() {
</div>
<div ref={boxRef} className='image-box' onClick={handleImageDivClick}>
{!preview && <p>Select Image</p>}
{preview && (
<div className='image-box' onClick={handleImageDivClick} disabled={loading}>
<img
src={preview}
alt="preview"
style={{ height: "400px" }}
style={{ maxHeight: "400px", height: "auto", userSelect: "none", maxWidth: "700px" }}
/>
)}
</div>
<div ref={scrollRef} style={{ paddingTop: "10px", marginTop: "30px", width: "85px", height: "35px", display: "flex", justifyContent: "center", minHeight: "25px", minWidth: "85px" }}>
{loading && <div className='loader'></div>}
</div>
<div className='response-block' style={{ width: "80%", margin: "40px", display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "10px" }}>
<div className='response-block' style={{ marginTop: "40px", width: "80%", margin: "40px", display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "10px" }}>
<div className='content-block class'>
<h3>Bird Species: </h3>
<p id='bird-class-text'>{birdClass}</p>
@@ -114,7 +118,7 @@ function Bird_CNN() {
</div>
</div>
<div className='explanation-box'>
<div className='explanation-box right'>
<h3>Model Architecture</h3>
<div style={{ display: "inline" }}>
<span>The model used for classification is a convolutional neural network (CNN) based on depthwise separable convolutions, as introduced in the </span>
@@ -123,7 +127,7 @@ function Bird_CNN() {
</div>
</div>
</div>
</div>
</div >
</>
);
}
+3 -4
View File
@@ -5,8 +5,7 @@
gap: 20px;
justify-content: center;
align-items: center;
background-color: rgb(33, 122, 194);
border: 1px solid black;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
background-color: rgb(25, 100, 126);
border-bottom-left-radius: 40px;
border-bottom-right-radius: 40px;
}
+19 -1
View File
@@ -1,3 +1,21 @@
html {
font-family: system-ui, sans-serif;
color: rgb(255, 255, 255);
font-size: 1rem;
line-height: 1.7;
}
body {
background: rgb(130, 170, 205);
background: rgb(4, 0, 10);
}
a {
color: rgb(255, 255, 255);
}
h1,
h2,
h3,
h4 {
font-family: 'Times New Roman', serif;
}