From 37eec59c93136d60ec8d3a4a5714de48771bb3ba Mon Sep 17 00:00:00 2001 From: MarcosG119 <86250436+MarcosG119@users.noreply.github.com> Date: Tue, 26 Dec 2023 12:48:19 -0800 Subject: [PATCH] Added plant identification and prepped deploy --- .github/workflows/firebase-hosting-merge.yml | 20 ++ .../firebase-hosting-pull-request.yml | 17 ++ firebase.json | 10 + package-lock.json | 195 ++++++++++++++++++ package.json | 4 + public/404.html | 33 +++ public/index.html | 89 ++++++++ server.js | 59 ++++++ .../PlantRecognition/PlantRecognition.css | 7 + .../PlantRecognition/PlantRecognition.tsx | 90 ++++++++ .../compounds/addPlant/AddPlant.css | 4 + .../compounds/addPlant/AddPlant.tsx | 71 +++++++ src/components/compounds/header/Header.css | 15 ++ .../searchContainer/SearchContainer.css | 52 +++++ .../searchContainer/SearchContainer.tsx | 4 +- .../elements/notesPopup/NotesContainer.tsx | 144 +++++++++++++ .../elements/notesPopup/NotesPopup.tsx | 45 ++++ src/components/elements/popup/Popup.css | 3 + src/components/elements/popup/Popup.tsx | 45 ++++ src/config/firebase.ts | 7 +- src/pages/identifyPlants/IdentifyPlants.tsx | 7 +- src/pages/myGarden/MyGarden.css | 12 ++ src/pages/myGarden/MyGarden.tsx | 111 +++++++--- .../userAuthentication/UserAuthentication.tsx | 2 + upload/images/images_1703294296146.jpg | Bin 0 -> 449729 bytes upload/images/images_1703294317202.jpg | Bin 0 -> 449729 bytes upload/images/images_1703294557708.jpg | Bin 0 -> 449729 bytes upload/images/images_1703294658332.jpg | Bin 0 -> 449729 bytes upload/images/images_1703294680926.jpg | Bin 0 -> 449729 bytes upload/images/images_1703294832345.jpg | Bin 0 -> 449729 bytes upload/images/images_1703294929384.jpg | Bin 0 -> 449729 bytes upload/images/images_1703294949910.jpg | Bin 0 -> 449729 bytes upload/images/images_1703295024106.jpg | Bin 0 -> 449729 bytes upload/images/images_1703295182697.jpg | Bin 0 -> 449729 bytes upload/images/images_1703295283501.jpg | Bin 0 -> 449729 bytes upload/images/images_1703295407463.jpg | Bin 0 -> 449729 bytes upload/images/images_1703295528025.jpg | Bin 0 -> 449729 bytes upload/images/images_1703295614877.jpg | Bin 0 -> 449729 bytes upload/images/images_1703295715476.jpg | Bin 0 -> 449729 bytes upload/images/images_1703295894296.jpg | Bin 0 -> 449729 bytes upload/images/images_1703296055908.jpg | Bin 0 -> 449729 bytes upload/images/images_1703296193619.jpg | Bin 0 -> 449729 bytes upload/images/images_1703296315393.jpg | Bin 0 -> 449729 bytes upload/images/images_1703296479835.jpg | Bin 0 -> 449729 bytes upload/images/images_1703296505499.jpg | Bin 0 -> 449729 bytes upload/images/images_1703296759290.jpg | Bin 0 -> 449729 bytes upload/images/images_1703296988220.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297030493.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297093749.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297137057.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297168804.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297224642.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297299051.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297315005.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297391871.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297436101.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297476854.jpg | Bin 0 -> 449729 bytes upload/images/images_1703297888372.jpg | Bin 0 -> 342452 bytes upload/images/images_1703297927786.jpg | Bin 0 -> 342452 bytes upload/images/images_1703298008155.jpg | Bin 0 -> 342452 bytes upload/images/images_1703298148892.jpg | Bin 0 -> 342452 bytes upload/images/images_1703298169798.jpg | Bin 0 -> 342452 bytes upload/images/images_1703298489396.jpg | Bin 0 -> 342452 bytes upload/images/images_1703298549244.jpg | Bin 0 -> 342452 bytes upload/images/images_1703298563451.jpg | Bin 0 -> 342452 bytes 65 files changed, 1015 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/firebase-hosting-merge.yml create mode 100644 .github/workflows/firebase-hosting-pull-request.yml create mode 100644 firebase.json create mode 100644 public/404.html create mode 100644 public/index.html create mode 100644 src/components/compounds/PlantRecognition/PlantRecognition.css create mode 100644 src/components/compounds/PlantRecognition/PlantRecognition.tsx create mode 100644 src/components/compounds/addPlant/AddPlant.css create mode 100644 src/components/compounds/addPlant/AddPlant.tsx create mode 100644 src/components/elements/notesPopup/NotesContainer.tsx create mode 100644 src/components/elements/notesPopup/NotesPopup.tsx create mode 100644 src/components/elements/popup/Popup.css create mode 100644 src/components/elements/popup/Popup.tsx create mode 100644 upload/images/images_1703294296146.jpg create mode 100644 upload/images/images_1703294317202.jpg create mode 100644 upload/images/images_1703294557708.jpg create mode 100644 upload/images/images_1703294658332.jpg create mode 100644 upload/images/images_1703294680926.jpg create mode 100644 upload/images/images_1703294832345.jpg create mode 100644 upload/images/images_1703294929384.jpg create mode 100644 upload/images/images_1703294949910.jpg create mode 100644 upload/images/images_1703295024106.jpg create mode 100644 upload/images/images_1703295182697.jpg create mode 100644 upload/images/images_1703295283501.jpg create mode 100644 upload/images/images_1703295407463.jpg create mode 100644 upload/images/images_1703295528025.jpg create mode 100644 upload/images/images_1703295614877.jpg create mode 100644 upload/images/images_1703295715476.jpg create mode 100644 upload/images/images_1703295894296.jpg create mode 100644 upload/images/images_1703296055908.jpg create mode 100644 upload/images/images_1703296193619.jpg create mode 100644 upload/images/images_1703296315393.jpg create mode 100644 upload/images/images_1703296479835.jpg create mode 100644 upload/images/images_1703296505499.jpg create mode 100644 upload/images/images_1703296759290.jpg create mode 100644 upload/images/images_1703296988220.jpg create mode 100644 upload/images/images_1703297030493.jpg create mode 100644 upload/images/images_1703297093749.jpg create mode 100644 upload/images/images_1703297137057.jpg create mode 100644 upload/images/images_1703297168804.jpg create mode 100644 upload/images/images_1703297224642.jpg create mode 100644 upload/images/images_1703297299051.jpg create mode 100644 upload/images/images_1703297315005.jpg create mode 100644 upload/images/images_1703297391871.jpg create mode 100644 upload/images/images_1703297436101.jpg create mode 100644 upload/images/images_1703297476854.jpg create mode 100644 upload/images/images_1703297888372.jpg create mode 100644 upload/images/images_1703297927786.jpg create mode 100644 upload/images/images_1703298008155.jpg create mode 100644 upload/images/images_1703298148892.jpg create mode 100644 upload/images/images_1703298169798.jpg create mode 100644 upload/images/images_1703298489396.jpg create mode 100644 upload/images/images_1703298549244.jpg create mode 100644 upload/images/images_1703298563451.jpg diff --git a/.github/workflows/firebase-hosting-merge.yml b/.github/workflows/firebase-hosting-merge.yml new file mode 100644 index 0000000..d73d55e --- /dev/null +++ b/.github/workflows/firebase-hosting-merge.yml @@ -0,0 +1,20 @@ +# This file was auto-generated by the Firebase CLI +# https://github.com/firebase/firebase-tools + +name: Deploy to Firebase Hosting on merge +'on': + push: + branches: + - main +jobs: + build_and_deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: npm ci && npm run build + - uses: FirebaseExtended/action-hosting-deploy@v0 + with: + repoToken: '${{ secrets.GITHUB_TOKEN }}' + firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_PLANT_PAPA }}' + channelId: live + projectId: plant-papa diff --git a/.github/workflows/firebase-hosting-pull-request.yml b/.github/workflows/firebase-hosting-pull-request.yml new file mode 100644 index 0000000..bc40960 --- /dev/null +++ b/.github/workflows/firebase-hosting-pull-request.yml @@ -0,0 +1,17 @@ +# This file was auto-generated by the Firebase CLI +# https://github.com/firebase/firebase-tools + +name: Deploy to Firebase Hosting on PR +'on': pull_request +jobs: + build_and_preview: + if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: npm ci && npm run build + - uses: FirebaseExtended/action-hosting-deploy@v0 + with: + repoToken: '${{ secrets.GITHUB_TOKEN }}' + firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_PLANT_PAPA }}' + projectId: plant-papa diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..e782939 --- /dev/null +++ b/firebase.json @@ -0,0 +1,10 @@ +{ + "hosting": { + "public": "public", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ] + } +} diff --git a/package-lock.json b/package-lock.json index 1826e20..e655559 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,14 @@ "@mui/material": "^5.15.0", "@types/react-router-dom": "^5.3.3", "axios": "^1.6.2", + "compressorjs": "^1.2.1", "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", "firebase": "^10.7.1", + "form-data": "^4.0.0", + "image-size": "^1.0.2", + "multer": "^1.4.5-lts.1", "node-fetch": "^3.3.2", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -2535,6 +2539,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2590,6 +2599,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/blueimp-canvas-to-blob": { + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz", + "integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -2648,6 +2662,22 @@ "node": ">=8" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2741,12 +2771,35 @@ "node": ">= 0.8" } }, + "node_modules/compressorjs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/compressorjs/-/compressorjs-1.2.1.tgz", + "integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==", + "dependencies": { + "blueimp-canvas-to-blob": "^3.29.0", + "is-blob": "^2.1.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -2784,6 +2837,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -3775,6 +3833,20 @@ "node": ">= 4" } }, + "node_modules/image-size": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz", + "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3827,6 +3899,17 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, + "node_modules/is-blob": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz", + "integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-core-module": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", @@ -3885,6 +3968,11 @@ "node": ">=8" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4097,12 +4185,48 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -4389,6 +4513,11 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4467,6 +4596,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4582,6 +4719,25 @@ "react-dom": ">=16.6.0" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", @@ -4885,6 +5041,27 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -5036,6 +5213,11 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -5082,6 +5264,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -5219,6 +5406,14 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 176fbfb..4b18fce 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,14 @@ "@mui/material": "^5.15.0", "@types/react-router-dom": "^5.3.3", "axios": "^1.6.2", + "compressorjs": "^1.2.1", "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", "firebase": "^10.7.1", + "form-data": "^4.0.0", + "image-size": "^1.0.2", + "multer": "^1.4.5-lts.1", "node-fetch": "^3.3.2", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..829eda8 --- /dev/null +++ b/public/404.html @@ -0,0 +1,33 @@ + + + + + + Page Not Found + + + + +
+

404

+

Page Not Found

+

The specified file was not found on this website. Please check the URL for mistakes and try again.

+

Why am I seeing this?

+

This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html file in your project's configured public directory.

+
+ + diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..e368b45 --- /dev/null +++ b/public/index.html @@ -0,0 +1,89 @@ + + + + + + Welcome to Firebase Hosting + + + + + + + + + + + + + + + + + + + +
+

Welcome

+

Firebase Hosting Setup Complete

+

You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!

+ Open Hosting Documentation +
+

Firebase SDK Loading…

+ + + + diff --git a/server.js b/server.js index 56f9983..7642b2c 100644 --- a/server.js +++ b/server.js @@ -4,14 +4,21 @@ import express from 'express'; import cors from 'cors'; import axios from 'axios'; import process from 'process'; +import FormData from 'form-data'; +import multer from 'multer'; +import path from 'path'; +import fs from 'fs'; const app = express(); const port = 3000; const TREFLE_API_TOKEN = process.env.TREFLE_API_KEY; +const PLANTNET_API_KEY = process.env.REACT_APP_PLANTNET_API_KEY; // Enable CORS app.use(cors()); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); // Define your proxy endpoint app.get('/api/plants/search', async (req, res) => { @@ -52,6 +59,58 @@ app.get('/api/plants/:plantId', async (req, res) => { } }); + + +const destination = "upload/images"; + +const diskStorage = multer.diskStorage({ + destination: destination, + filename: (req, file, cb) => { + return cb(null, `${file.fieldname}_${Date.now()}${path.extname(file.originalname)}`); + }, +}); + +const upload = multer({ + storage: diskStorage, + limits: { + fileSize: 1000000, + }, +}); + +app.post('/api/plantnet/upload', upload.single("images"), async (req, res) => { + try { + const formData = new FormData(); + + console.log('Request Body:', req.body); + console.log('Request File:', req.file); + console.log('Form Data:', formData); + + + formData.append('organs', 'flower'); + formData.append('images', fs.createReadStream(req.file.path)); + + if (!req.file) { + console.error('No file provided in the request'); + return res.status(400).json({ error: 'No file provided' }); + } + + const project = 'all'; + + const { status, data } = await axios.post('https://my-api.plantnet.org/v2/identify/' + project + `?api-key=${PLANTNET_API_KEY}`, formData, { + headers: formData.getHeaders(), + }); + + console.log('status', status); + console.log('data', data); + + console.log('Identification status: ', status); + res.json(data); + } catch (error) { + console.error('Server error uploading image:', error); + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + app.listen(port, () => { console.log(`Server is running on port ${port}`); }); \ No newline at end of file diff --git a/src/components/compounds/PlantRecognition/PlantRecognition.css b/src/components/compounds/PlantRecognition/PlantRecognition.css new file mode 100644 index 0000000..6aa2846 --- /dev/null +++ b/src/components/compounds/PlantRecognition/PlantRecognition.css @@ -0,0 +1,7 @@ +.flex-box { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100%;; +} \ No newline at end of file diff --git a/src/components/compounds/PlantRecognition/PlantRecognition.tsx b/src/components/compounds/PlantRecognition/PlantRecognition.tsx new file mode 100644 index 0000000..ac6fa19 --- /dev/null +++ b/src/components/compounds/PlantRecognition/PlantRecognition.tsx @@ -0,0 +1,90 @@ +import React, { useState, ChangeEvent, useEffect } from 'react'; +import Compressor from 'compressorjs'; +import axios from 'axios'; +import './PlantRecognition.css' +import Container from '../../elements/container/Container'; + +const ImageUpload: React.FC = () => { + const [selectedFile, setSelectedFile] = useState(null); + const [bestMatch, setBestMatch] = useState(null); + const [loading, setLoading] = useState(false); + const [uploadedImage, setUploadedImage] = useState(null); + + useEffect(() => { + console.log(uploadedImage) + if (selectedFile) { + const reader = new FileReader(); + reader.onloadend = () => { + setUploadedImage(reader.result as string); + }; + reader.readAsDataURL(selectedFile); + } + }, [selectedFile]); + + const handleFileChange = (event: ChangeEvent) => { + if (event.target.files && event.target.files.length > 0) { + setSelectedFile(event.target.files[0]); + } + }; + + const handleUpload = async () => { + try { + setLoading(true); + + // Compress the selected image with specified dimensions and quality + const compressedImage = await compressImage(selectedFile, 800, 1280, 0.9); + + // Create FormData with the compressed image + const formData = new FormData(); + formData.append('organs', 'flower'); + formData.append('images', compressedImage, 'compressed.jpg'); + + const { status, data } = await axios.post('http://localhost:3000/api/plantnet/upload', formData); + + console.log('Response status:', status); + console.log('Response data:', data); + + setBestMatch(data.bestMatch); + } catch (error) { + console.error('Frontend error uploading image:', error); + } finally { + setLoading(false); + } + }; + + const compressImage = (file: File | null, smallSide: number, largeSide: number, quality: number): Promise => { + return new Promise((resolve, reject) => { + if (!file) { + reject(new Error('No file provided')); + return; + } + + new Compressor(file, { + maxWidth: largeSide, + maxHeight: smallSide, + quality, + success(result) { + resolve(result); + }, + error(err) { + reject(err); + }, + }); + }); + }; + + return ( + +
+ + + {loading &&

Uploading and processing image...

} + {bestMatch &&

{bestMatch}

} + {uploadedImage && Uploaded} + +
+
+ ); +}; + +export default ImageUpload; diff --git a/src/components/compounds/addPlant/AddPlant.css b/src/components/compounds/addPlant/AddPlant.css new file mode 100644 index 0000000..c9f93bf --- /dev/null +++ b/src/components/compounds/addPlant/AddPlant.css @@ -0,0 +1,4 @@ +.add-plant { + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/src/components/compounds/addPlant/AddPlant.tsx b/src/components/compounds/addPlant/AddPlant.tsx new file mode 100644 index 0000000..4d097cb --- /dev/null +++ b/src/components/compounds/addPlant/AddPlant.tsx @@ -0,0 +1,71 @@ +import React, { useState } from 'react'; +import Container from '../../elements/container/Container'; +import Button from '../../elements/button/Button'; + +import { doc, setDoc } from "firebase/firestore"; +import { auth, db } from '../../../config/firebase.ts'; + +import { useNavigate } from 'react-router-dom'; + + +const AddPlant: React.FC = () => { + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const [picture, setPicture] = useState(''); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [scientificName, setScientificName] = useState(''); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [id, setId] = useState(''); + + const navigate = useNavigate(); + + const handleClick = async () => { + try{ + setPicture("https://images.thdstatic.com/productImages/5ed4957b-8edf-4b0f-b4fb-3d6f48a78caa/svn/green-art-prints-julnpp02nfpfl12-64_600.jpg"); + const userId = auth?.currentUser?.uid as string; + const uniquePlantId = new Date().getTime().toString(); + + await setDoc(doc(db, "myGarden", userId), { + [uniquePlantId]: { + plantName: name, + scientificName: scientificName, + picture: picture, + description: description, + id: id, + uniquePlantId: uniquePlantId, + lastWatered: "", + notes: [] + } + }, {merge: true}); + + navigate('/my-garden'); + } catch(error){ + console.error(error); + } + } + + return ( + + +
+ setName(e.target.value)} + /> +

+