Skip to content

Commit

Permalink
Merge branch 'main' into file_upload
Browse files Browse the repository at this point in the history
  • Loading branch information
jason490 committed Jul 21, 2024
2 parents 86de725 + 2aaae54 commit 9082d6f
Show file tree
Hide file tree
Showing 18 changed files with 554 additions and 22 deletions.
19 changes: 19 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,20 @@
* text eol=lf
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary
9 changes: 9 additions & 0 deletions client/app/components/BackButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import styles from '../components/BackButton.module.css';

export default function ArrowButton({ onClick, children }) {
return (
<button className={styles.backButton} onClick={onClick}>
<span className={styles.arrow}>&larr;</span> {children}
</button>
);
}
25 changes: 25 additions & 0 deletions client/app/components/BackButton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.backButton {
align-self: flex-start;
padding: 5px 10px;
margin: 10px;
border-radius: 8px;
border: none;
background-color: #c5f687;
color: white;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;

position: relative;
display: flex;
align-items: center;
}

.backButton:hover {
background-color: #a5d667;
}

.arrow {
display: inline-block;
font-size: 1.5em;
}
116 changes: 116 additions & 0 deletions client/app/components/Camera.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, { useState, useEffect, useRef } from 'react';
import styles from './Camera.module.css';

const Camera = () => {
const videoRef = useRef(null);
const canvasRef = useRef(null);
const [devices, setDevices] = useState([]);
const [selectedDeviceId, setSelectedDeviceId] = useState('');
const [photo, setPhoto] = useState('');

useEffect(() => {
const getDevices = async () => {
const allDevices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = allDevices.filter(device => device.kind === 'videoinput');
setDevices(videoDevices);
if (videoDevices.length > 0) {
setSelectedDeviceId(videoDevices[0].deviceId);
}
};

getDevices();
}, []);

const startCamera = async () => {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { deviceId: selectedDeviceId ? { exact: selectedDeviceId } : undefined }
});
if (videoRef.current) {
videoRef.current.srcObject = stream;
videoRef.current.play();
}
} catch (err) {
console.error('Error accessing the camera: ', err);
}
}
};

useEffect(() => {
if (selectedDeviceId) {
startCamera();
}

return () => {
if (videoRef.current && videoRef.current.srcObject) {
videoRef.current.srcObject.getTracks().forEach((track) => track.stop());
}
};
}, [selectedDeviceId]);

const takePhoto = () => {
const width = videoRef.current.videoWidth;
const height = videoRef.current.videoHeight;

const context = canvasRef.current.getContext('2d');
canvasRef.current.width = width;
canvasRef.current.height = height;
context.drawImage(videoRef.current, 0, 0, width, height);

const dataUrl = canvasRef.current.toDataURL('image/png');
setPhoto(dataUrl);
};

const uploadPhoto = async () => {
const response = await fetch('/api/upload', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ image: photo }),
});

const result = await response.json();
console.log(result);
};

const retakePhoto = () => {
setPhoto('');
startCamera(); // Restart the camera when retaking a photo
};

return (
<div className={styles.container}>
{!photo && (
<div className={styles.wrapper}>
<select
onChange={(e) => setSelectedDeviceId(e.target.value)}
value={selectedDeviceId}
className={styles.select}
>
{devices.map(device => (
<option key={device.deviceId} value={device.deviceId}>
{device.label || `Camera ${device.deviceId}`}
</option>
))}
</select>
<video ref={videoRef} className={styles.video} />
<button className={styles.button} onClick={takePhoto}>Take Photo</button>
</div>
)}
{photo && (
<div className={styles.wrapper}>
<img src={photo} alt="Captured" className={styles.image} />
<div className={styles.buttonWrapper}>
<button className={styles.button} onClick={uploadPhoto}>Upload Photo</button>
<button className={styles.button} onClick={retakePhoto}>Retake Photo</button>
</div>
</div>
)}
<canvas ref={canvasRef} style={{ display: 'none' }}></canvas>
</div>
);
};

export default Camera;
57 changes: 57 additions & 0 deletions client/app/components/Camera.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.video {
width: 100%;
max-width: 80vw;
border: 1px solid #ccc;
border-radius: 8px;
}

.select {
padding: 10px;
margin: 10px 0;
border-radius: 8px;
border: 1px solid #ccc;
font-size: 16px;
}

.buttonWrapper {
display: flex;
align-items: center;
justify-content: center;
}

.button {
padding: 10px 20px;
margin: 20px 10px;
border-radius: 8px;
border: none;
background-color: #c5f687;
color: white;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}

.button:hover {
background-color: #a5d667;
}

.image {
width: 100%;
max-width: 600px;
border: 1px solid #ccc;
border-radius: 8px;
}
44 changes: 44 additions & 0 deletions client/app/components/ScanInsectPlant.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
background-color: #fff;
padding: 20px;
font-family: 'Inter';
}

.title {
font-size: 24px;
margin: 20px 0;
color: #B3E576;
}

.imageContainer {
display: flex;
align-items: center;
justify-content: center;
width: 350px;
border: 1px solid #ccc;
border-radius: 10px;
}

.selectedImage {
width: 100%;
height: auto;
}

.placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
width: 100%;
color: #B3E576;
}

.fileInput {
margin-top: 20px;
}

Empty file removed client/app/components/temp
Empty file.
Binary file modified client/app/images/insect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/app/images/placeholder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/app/images/pollination.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/app/images/scan-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions client/app/pages/api/upload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// pages/api/upload.js
import fs from 'fs';
import path from 'path';

export default async (req, res) => {
if (req.method === 'POST') {
const { image } = req.body;

// Create a buffer from the base64 image string
const buffer = Buffer.from(image.split(',')[1], 'base64');

// Define the path where the image will be saved
const uploadDir = path.join(process.cwd(), 'public', 'uploads');
const filePath = path.join(uploadDir, `${Date.now()}.png`);

// Ensure the uploads directory exists
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}

// Write the buffer to a file
fs.writeFileSync(filePath, buffer);

return res.status(200).json({ message: 'Upload successful', filePath });
}

return res.status(405).json({ message: 'Method not allowed' });
};
Loading

0 comments on commit 9082d6f

Please sign in to comment.