diff --git a/.gitattributes b/.gitattributes
index fcadb2c..1e80435 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -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
diff --git a/client/app/components/BackButton.js b/client/app/components/BackButton.js
new file mode 100644
index 0000000..556c447
--- /dev/null
+++ b/client/app/components/BackButton.js
@@ -0,0 +1,9 @@
+import styles from '../components/BackButton.module.css';
+
+export default function ArrowButton({ onClick, children }) {
+ return (
+
+ );
+}
diff --git a/client/app/components/BackButton.module.css b/client/app/components/BackButton.module.css
new file mode 100644
index 0000000..9abf72c
--- /dev/null
+++ b/client/app/components/BackButton.module.css
@@ -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;
+}
\ No newline at end of file
diff --git a/client/app/components/Camera.js b/client/app/components/Camera.js
new file mode 100644
index 0000000..b24d843
--- /dev/null
+++ b/client/app/components/Camera.js
@@ -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 (
+
+ {!photo && (
+
+
+
+
+
+ )}
+ {photo && (
+
+
+
+
+
+
+
+ )}
+
+
+ );
+};
+
+export default Camera;
diff --git a/client/app/components/Camera.module.css b/client/app/components/Camera.module.css
new file mode 100644
index 0000000..3b5efa3
--- /dev/null
+++ b/client/app/components/Camera.module.css
@@ -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;
+}
diff --git a/client/app/components/ScanInsectPlant.module.css b/client/app/components/ScanInsectPlant.module.css
new file mode 100644
index 0000000..5ca7f50
--- /dev/null
+++ b/client/app/components/ScanInsectPlant.module.css
@@ -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;
+ }
+
\ No newline at end of file
diff --git a/client/app/components/temp b/client/app/components/temp
deleted file mode 100644
index e69de29..0000000
diff --git a/client/app/images/insect.png b/client/app/images/insect.png
index 6fb349c..cb50531 100644
Binary files a/client/app/images/insect.png and b/client/app/images/insect.png differ
diff --git a/client/app/images/placeholder.png b/client/app/images/placeholder.png
new file mode 100644
index 0000000..f878f8d
Binary files /dev/null and b/client/app/images/placeholder.png differ
diff --git a/client/app/images/pollination.png b/client/app/images/pollination.png
index 6fb349c..8c71dbf 100644
Binary files a/client/app/images/pollination.png and b/client/app/images/pollination.png differ
diff --git a/client/app/images/scan-icon.png b/client/app/images/scan-icon.png
index 1364d2f..4437863 100644
Binary files a/client/app/images/scan-icon.png and b/client/app/images/scan-icon.png differ
diff --git a/client/app/pages/api/upload.js b/client/app/pages/api/upload.js
new file mode 100644
index 0000000..f63ebc3
--- /dev/null
+++ b/client/app/pages/api/upload.js
@@ -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' });
+};
diff --git a/client/app/saved-species/page.js b/client/app/saved-species/page.js
new file mode 100644
index 0000000..03fbb3d
--- /dev/null
+++ b/client/app/saved-species/page.js
@@ -0,0 +1,135 @@
+// app/saved-species/saved-species.js
+'use client';
+import { useRouter } from 'next/navigation';
+import placeholder from '../images/pollination.png';
+
+export default function SavedSpeciesPage() {
+ const router = useRouter();
+
+ const handleGoBackButton = () => {
+ console.log('Go Back Button clicked');
+ router.push('/scan-species');
+ };
+
+ return (
+
+
+
+ Saved List
+
+
+
+ Plants
+
+
+ Insects
+
+
+
+
+ {/* Placeholder images for Plants */}
+ {Array.from({ length: 12 }).map((_, index) => (
+
+
+ ))}
+
+
+ {/* Placeholder images for Insects */}
+
+
+
+ {Array.from({ length: 11 }).map((_, index) => (
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/client/app/scan-insect/page.js b/client/app/scan-insect/page.js
new file mode 100644
index 0000000..15748c8
--- /dev/null
+++ b/client/app/scan-insect/page.js
@@ -0,0 +1,51 @@
+'use client';
+import { useRouter } from 'next/navigation';
+import Image from 'next/image';
+import scanIcon from '../images/scan-icon.png';
+import Camera from '../components/Camera';
+import { useState } from 'react';
+import styles from '../components/ScanInsectPlant.module.css';
+import BackButton from '../components/BackButton';
+
+export default function ScanInsectPage() {
+ const router = useRouter();
+ const [selectedImage, setSelectedImage] = useState(null);
+
+ const handleFileChange = (event) => {
+ const file = event.target.files[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = () => {
+ setSelectedImage(reader.result);
+ };
+ reader.readAsDataURL(file);
+ }
+ };
+
+ const handleBack = () => {
+ router.push('/scan-species');
+ };
+
+ return (
+
+
+
Take Photo or Choose Existing Image
+
+ {selectedImage ? (
+
+ ) : (
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/client/app/scan-plant/page.js b/client/app/scan-plant/page.js
new file mode 100644
index 0000000..15748c8
--- /dev/null
+++ b/client/app/scan-plant/page.js
@@ -0,0 +1,51 @@
+'use client';
+import { useRouter } from 'next/navigation';
+import Image from 'next/image';
+import scanIcon from '../images/scan-icon.png';
+import Camera from '../components/Camera';
+import { useState } from 'react';
+import styles from '../components/ScanInsectPlant.module.css';
+import BackButton from '../components/BackButton';
+
+export default function ScanInsectPage() {
+ const router = useRouter();
+ const [selectedImage, setSelectedImage] = useState(null);
+
+ const handleFileChange = (event) => {
+ const file = event.target.files[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = () => {
+ setSelectedImage(reader.result);
+ };
+ reader.readAsDataURL(file);
+ }
+ };
+
+ const handleBack = () => {
+ router.push('/scan-species');
+ };
+
+ return (
+
+
+
Take Photo or Choose Existing Image
+
+ {selectedImage ? (
+
+ ) : (
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/client/app/scan-species/page.js b/client/app/scan-species/page.js
index 9389e87..7e35fde 100644
--- a/client/app/scan-species/page.js
+++ b/client/app/scan-species/page.js
@@ -8,14 +8,14 @@ export default function ScanSpeciesPage() {
const router = useRouter();
const handleScanPlant = () => {
- // Add your scan plant logic here
- console.log('Scan a Plant clicked');
+ console.log('Scan an Insect clicked');
+ router.push('/scan-plant')
};
const handleScanInsect = () => {
// Add your scan insect logic here
console.log('Scan an Insect clicked');
- router.push('/species-information/insect-information')
+ router.push('/scan-insect')
};
const handleSavedSpecies = () => {
diff --git a/client/package-lock.json b/client/package-lock.json
index 0060c03..3996380 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -14,7 +14,7 @@
},
"devDependencies": {
"eslint": "^8",
- "eslint-config-next": "14.2.4"
+ "eslint-config-next": "14.2.5"
}
},
"node_modules/@babel/runtime": {
@@ -168,9 +168,9 @@
"integrity": "sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg=="
},
"node_modules/@next/eslint-plugin-next": {
- "version": "14.2.4",
- "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.4.tgz",
- "integrity": "sha512-svSFxW9f3xDaZA3idQmlFw7SusOuWTpDTAeBlO3AEPDltrraV+lqs7mAc6A27YdnpQVVIA3sODqUAAHdWhVWsA==",
+ "version": "14.2.5",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.5.tgz",
+ "integrity": "sha512-LY3btOpPh+OTIpviNojDpUdIbHW9j0JBYBjsIp8IxtDFfYFyORvw3yNq6N231FVqQA7n7lwaf7xHbVJlA1ED7g==",
"dev": true,
"dependencies": {
"glob": "10.3.10"
@@ -1344,12 +1344,12 @@
}
},
"node_modules/eslint-config-next": {
- "version": "14.2.4",
- "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.4.tgz",
- "integrity": "sha512-Qr0wMgG9m6m4uYy2jrYJmyuNlYZzPRQq5Kvb9IDlYwn+7yq6W6sfMNFgb+9guM1KYwuIo6TIaiFhZJ6SnQ/Efw==",
+ "version": "14.2.5",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.5.tgz",
+ "integrity": "sha512-zogs9zlOiZ7ka+wgUnmcM0KBEDjo4Jis7kxN1jvC0N4wynQ2MIx/KBkg4mVF63J5EK4W0QMCn7xO3vNisjaAoA==",
"dev": true,
"dependencies": {
- "@next/eslint-plugin-next": "14.2.4",
+ "@next/eslint-plugin-next": "14.2.5",
"@rushstack/eslint-patch": "^1.3.3",
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0",
"eslint-import-resolver-node": "^0.3.6",
@@ -1974,9 +1974,9 @@
}
},
"node_modules/glob/node_modules/minimatch": {
- "version": "9.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
- "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -2741,13 +2741,10 @@
}
},
"node_modules/lru-cache": {
- "version": "10.2.2",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
- "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
- "dev": true,
- "engines": {
- "node": "14 || >=16.14"
- }
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true
},
"node_modules/merge2": {
"version": "1.4.1",
diff --git a/client/package.json b/client/package.json
index b34c906..0298dfd 100644
--- a/client/package.json
+++ b/client/package.json
@@ -15,6 +15,6 @@
},
"devDependencies": {
"eslint": "^8",
- "eslint-config-next": "14.2.4"
+ "eslint-config-next": "14.2.5"
}
}