+ {error &&
{error}
}
-
diff --git a/frontend/src/Components/auth/auth.js b/frontend/src/Components/auth/auth.js
index 49b039e..97428a8 100644
--- a/frontend/src/Components/auth/auth.js
+++ b/frontend/src/Components/auth/auth.js
@@ -23,7 +23,8 @@ const patterns = {
export const validationFunctions = {
checkEmpty: (name = "", value1 = "", value2 = "") =>
value1.length > 0 || `${errorLabels[name]} field is required`,
-
+ noSpaces: (name = "", value1 = "", value2 = "") =>
+ !value1.includes(" ") || `${errorLabels[name]} cannot have any spaces`,
checkPasswordLength: (name = "", value1 = "", value2 = "") =>
value1.length >= 8 ||
value1.length === 0 ||
@@ -37,21 +38,22 @@ export const validationFunctions = {
return regex.test(value1) || value1.length === 0 ? true : "Invalid email";
},
- checkboxRequired: (name="", value1=false, value2="") =>{
- return value1 || "This is required."
- }
+ checkboxRequired: (name = "", value1 = false, value2 = "") => {
+ return value1 || "This is required.";
+ },
};
// an object containing input fields (keys)
// and their associated validation funciton (values as an array)
export const formValidation = {
- username: [validationFunctions.checkEmpty],
+ username: [validationFunctions.checkEmpty, validationFunctions.noSpaces],
fname: [validationFunctions.checkEmpty],
lname: [validationFunctions.checkEmpty],
email: [validationFunctions.checkEmpty, validationFunctions.checkValidEmail],
password: [
validationFunctions.checkEmpty,
validationFunctions.checkPasswordLength,
+ validationFunctions.noSpaces,
],
oldPassword: [
validationFunctions.checkEmpty,
@@ -60,16 +62,17 @@ export const formValidation = {
newPassword: [
validationFunctions.checkEmpty,
validationFunctions.checkPasswordLength,
+ validationFunctions.noSpaces,
],
confirmPassword: [
validationFunctions.checkEmpty,
validationFunctions.checkPasswordMatch,
+ validationFunctions.noSpaces,
],
confirmNewPassword: [
validationFunctions.checkEmpty,
validationFunctions.checkPasswordMatch,
+ validationFunctions.noSpaces,
],
- confirmNewPasswordCheckbox: [
- validationFunctions.checkboxRequired
- ]
+ confirmNewPasswordCheckbox: [validationFunctions.checkboxRequired],
};
diff --git a/frontend/src/hooks/useBookmark.jsx b/frontend/src/hooks/useBookmark.jsx
new file mode 100644
index 0000000..7d0dfe0
--- /dev/null
+++ b/frontend/src/hooks/useBookmark.jsx
@@ -0,0 +1,52 @@
+import { useState } from "react";
+import { useAuthContext } from "./useAuthContext";
+import toast from "react-hot-toast";
+
+export default function useBookmark() {
+ const [error, setError] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const apiUrl = import.meta.env.VITE_API_URL;
+ const { user } = useAuthContext();
+
+ const updateBookmark = async (article_id, isBookmarked, toggleBookmark) => {
+ setError(null);
+ setIsLoading(true);
+
+ try {
+ if (!user) {
+ throw new Error("You need to be logged in first!");
+ }
+
+ const response = await fetch(
+ isBookmarked
+ ? `${apiUrl}/api/bookmarks/${article_id}`
+ : `${apiUrl}/api/bookmarks`,
+ {
+ method: isBookmarked ? "DELETE" : "POST",
+ headers: {
+ Authorization: `Bearer ${user.token}`,
+ "Content-Type": isBookmarked ? undefined : "application/json",
+ },
+ body: isBookmarked
+ ? undefined
+ : JSON.stringify({ article_id: article_id }),
+ }
+ );
+
+ const json = await response.json();
+ // console.log(json);
+ if (json.error) {
+ throw new Error(json.error || "Failed to toggle bookmark");
+ }
+
+ // Call the bookmark toggle function if the request is successful
+ toggleBookmark();
+ } catch (error) {
+ setError(error.message);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return { isLoading, error, updateBookmark };
+}
diff --git a/frontend/src/hooks/useDeleteMultipleBookmarks.jsx b/frontend/src/hooks/useDeleteMultipleBookmarks.jsx
new file mode 100644
index 0000000..a2a72aa
--- /dev/null
+++ b/frontend/src/hooks/useDeleteMultipleBookmarks.jsx
@@ -0,0 +1,46 @@
+import { useState } from "react";
+import { useAuthContext } from "./useAuthContext";
+
+export const useDeleteMultipleBookmarks = () => {
+ const [error, setError] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const apiUrl = import.meta.env.VITE_API_URL;
+ const { user } = useAuthContext();
+ //takes in list of bookmark ids to delete, method for deleting on client side
+ const deleteMultipleBookmarks = async (
+ bookmarkIds,
+ deleteBookmarksHandler
+ ) => {
+ setIsLoading(true);
+ setError(null);
+ try {
+ if (!user) {
+ throw new Error("You need to be logged in first!");
+ }
+
+ const response = await fetch(
+ `${apiUrl}/api/bookmarks/${bookmarkIds.join(",")}`,
+ {
+ method: "DELETE",
+ headers: {
+ Authorization: `Bearer ${user.token}`,
+ },
+ }
+ );
+
+ const json = await response.json();
+ if (!response.ok) {
+ setError(json.error);
+ }
+ if (response.ok) {
+ deleteBookmarksHandler();
+ }
+ } catch (err) {
+ setError(err.message);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return { deleteMultipleBookmarks, isLoading, error };
+};
diff --git a/frontend/src/hooks/useGetBookmarks.jsx b/frontend/src/hooks/useGetBookmarks.jsx
new file mode 100644
index 0000000..6839871
--- /dev/null
+++ b/frontend/src/hooks/useGetBookmarks.jsx
@@ -0,0 +1,47 @@
+import { useState } from "react";
+import { useAuthContext } from "./useAuthContext";
+
+export const useGetBookmarks = () => {
+ const [error, setError] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [bookmarks, setBookmarks] = useState([]);
+ const apiUrl = import.meta.env.VITE_API_URL;
+ const { user } = useAuthContext();
+
+ const getBookmarks = async () => {
+ setIsLoading(true);
+ setError(null);
+ try {
+
+ let {token} = JSON.parse(localStorage.getItem('user'))
+
+ if (!user && !token) {
+ throw new Error("You need to be logged in first!");
+ }
+
+ const response = await fetch(
+ `${apiUrl}/api/bookmarks`,
+ {
+ method: "GET",
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ );
+
+ const json = await response.json();
+ if (!response.ok) {
+ throw new Error("Failed to retrieve bookmarks");
+ }
+ if (response.ok) {
+ setBookmarks(json)
+ }
+ } catch (err) {
+ setError(err.message);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return { getBookmarks,bookmarks, isLoading, error };
+};
diff --git a/frontend/src/hooks/useLogin.jsx b/frontend/src/hooks/useLogin.jsx
index b821e2c..aa99248 100644
--- a/frontend/src/hooks/useLogin.jsx
+++ b/frontend/src/hooks/useLogin.jsx
@@ -1,23 +1,32 @@
import { useState } from "react";
import { useAuthContext } from "./useAuthContext";
+import { Navigate, useNavigate } from "react-router-dom";
+
export const useLogin = () =>
{
const [error, setError] = useState(null)
- const [isLoading, setIsLoading] = useState(null)
+ const [isLoading, setIsLoading] = useState(false)
const { dispatch } = useAuthContext()
-
- const login = async (username,user_password) =>
+ const navigate = useNavigate()
+ const apiUrl = import.meta.env.VITE_API_URL; //change to .env
+ // const api = process.env.REACT_APP_API_URL
+
+
+ const login = async (username,user_password,error) =>
{
setIsLoading(true);
setError(null);
-
- const response = await fetch('http://localhost:3002/api/users/login',
+ try {
+ const response = await fetch(`http://localhost:3002/api/users/login`,
{
method : 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ username, user_password })
+
})
+ console.log(apiUrl)
+
const json = await response.json();
@@ -33,8 +42,16 @@ export const useLogin = () =>
dispatch({type: 'LOGIN', payload: json})
setIsLoading(false)
+ console.log(localStorage)
+ navigate("/")
}
+
+ }
+ catch (err) {
+ setError("Something went wrong. Please try again.");
+ setIsLoading(false);
}
+}
return {login, isLoading, error}
}
\ No newline at end of file
diff --git a/frontend/src/hooks/useSignup.jsx b/frontend/src/hooks/useSignup.jsx
index e9d989c..c04fecc 100644
--- a/frontend/src/hooks/useSignup.jsx
+++ b/frontend/src/hooks/useSignup.jsx
@@ -1,40 +1,57 @@
import { useState } from "react";
import { useAuthContext } from "./useAuthContext";
+import { useNavigate } from "react-router-dom";
-export const useSignup = () =>
-{
- const [error, setError] = useState(null)
- const [isLoading, setIsLoading] = useState(null)
- const { dispatch } = useAuthContext()
- const signup = async (username,user_password) =>
- {
- setIsLoading(true);
- setError(null);
+export const useSignup = () => {
+ const [error, setError] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const { dispatch } = useAuthContext();
+ const navigate = useNavigate();
+ const apiUrl = import.meta.env.VITE_API_URL;
+ // const api = process.env.REACT_APP_API_URL
+
- const response = await fetch('http://localhost:3002/api/users/',
- {
- method : 'POST',
- headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({ username, user_password })
- })
+ console.log("API URL from environment:", apiUrl); // Debug log
- const json = await response.json();
-
- if (!response.ok) {
- setIsLoading(false)
- setError(json.error)
- }
- if (response.ok) {
- //save user to local storage
- localStorage.setItem('user', JSON.stringify(json))
-
- //update authcontext
- dispatch({type: 'LOGIN', payload: json})
+ const signup = async (username, user_password) => {
+ setIsLoading(true);
+ setError(null);
- setIsLoading(false)
+ try {
+ const response = await fetch(`http://localhost:3002/api/users`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ username, user_password })
+ });
+
+ let json;
+ try {
+ json = await response.json();
+ } catch (err) {
+ setError("Invalid response from server");
+ setIsLoading(false);
+ return;
+ }
+
+ if (!response.ok) {
+ setError(json.error);
+ setIsLoading(false);
+ return;
+ }
+
+ // Save user to local storage
+ localStorage.setItem('user', JSON.stringify(json));
+
+ // Update auth context
+ dispatch({ type: 'LOGIN', payload: json });
+
+ navigate("/signin");
+ } catch (err) {
+ setError("Something went wrong. Please try again.");
+ setIsLoading(false);
}
- }
+ };
- return {signup, isLoading, error}
-}
\ No newline at end of file
+ return { signup, isLoading, error };
+};
diff --git a/frontend/src/pages/ArticleResultsPage/ArticleResultsPage.jsx b/frontend/src/pages/ArticleResultsPage/ArticleResultsPage.jsx
index 2ee102a..8705975 100644
--- a/frontend/src/pages/ArticleResultsPage/ArticleResultsPage.jsx
+++ b/frontend/src/pages/ArticleResultsPage/ArticleResultsPage.jsx
@@ -1,9 +1,12 @@
import { useEffect, useState } from "react";
-import { useParams, useSearchParams } from "react-router-dom";
-import RelatedTags from "../../Components/ArticleResults/SideSections/RelatedTopicTags/RelatedTopicTags.jsx";
-import ArticleResultsList from "../../Components/ArticleResults/ArticleResultsList.jsx";
import { Col, Container, Row } from "react-bootstrap";
+import toast from "react-hot-toast";
+import { useSearchParams } from "react-router-dom";
+import ArticleResult from "../../Components/ArticleResults/ArticleResult/ArticleResult.jsx";
+import RelatedTags from "../../Components/ArticleResults/SideSections/RelatedTopicTags/RelatedTopicTags.jsx";
+import useBookmark from "../../hooks/useBookmark.jsx";
import "./ArticleResultsPage.scss";
+import { AiOutlineLoading3Quarters } from "react-icons/ai";
const dummy_topic_tags = [
{ label: "Deep Learning" },
{ label: "Artifical Intelligence" },
@@ -23,9 +26,14 @@ const dummmy_articles = [
];
export default function ArticleResultsPage({}) {
- const [articles, setArticles] = useState();
+ const { error, updateBookmark } = useBookmark();
+
+ // keep track of a specific article's bookmark toggling process
+ const [pendingArticles, setPendingArticles] = useState({});
+
+ const [articles, setArticles] = useState([]);
const [searchParams, setSearchParams] = useSearchParams();
- const [specificArticle, setSpecificArticle] = useState();
+ // const [specificArticle, setSpecificArticle] = useState();
const titleQuery = searchParams.get("title");
//bookmark toggler creator function, returns function that toggles bookmark for certain id
@@ -37,53 +45,131 @@ export default function ArticleResultsPage({}) {
!newArticles[articleIndex].isBookmarked;
setArticles(newArticles);
}
+
+ // set pending state of an article being bookmarked to true
+ setPendingArticles((prev) => ({ ...prev, [id]: true }));
+
+ // reset pending state to false
+ setTimeout(() => {
+ setPendingArticles((prev) => ({ ...prev, [id]: false }));
+ }, 500);
};
+ //holds whether article search has been attempted
+ const [searchAttempted, setSearchAttempted] = useState(false);
+
useEffect(() => {
- fetch(`http://localhost:3002/api/articles/?title=${titleQuery}`)
- .then((res) =>
- res.json().then((data) => {
- let dataCopy = [...data];
-
- //we dont have author names, date, bookmarked, or image, so just inserting default in for now
- dataCopy.forEach((articleObject) => {
- articleObject.image =
- "https://emeritus.org/in/wp-content/uploads/sites/3/2023/03/types-of-machine-learning.jpg.optimal.jpg";
- articleObject.author = "jeff";
- articleObject.date = "October 24, 2023";
- articleObject.isBookmarked = false;
- });
- setArticles(dataCopy);
- })
- )
- .catch((error) => {
- console.error("error fetching data");
- });
+ const authUser = localStorage.getItem("user");
+ const user = authUser ? JSON.parse(authUser) : undefined;
+
+ setSearchAttempted(false);
+
+ const fetchArticles = async () => {
+ try {
+ const response = await fetch(
+ `http://localhost:3002/api/articles/?title=${titleQuery}`,
+ {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: user ? `Bearer ${user.token}` : undefined,
+ },
+ }
+ );
+ const data = await response.json();
+
+ const enrichedData = data.map((articleObject) => ({
+ ...articleObject,
+ image:
+ "https://emeritus.org/in/wp-content/uploads/sites/3/2023/03/types-of-machine-learning.jpg.optimal.jpg",
+ author: "jeff",
+ date: "October 24, 2023",
+ isBookmarked: articleObject.isBookmarked
+ ? articleObject.isBookmarked
+ : false,
+ }));
+ setArticles(enrichedData);
+
+ // initialize pending state of all article results to false
+ let initPendingArticles = {};
+ enrichedData.forEach(({ id }) => {
+ initPendingArticles[id] = false;
+ });
+ setPendingArticles(initPendingArticles);
+ } catch (error) {
+ toast.error(error.message);
+ } finally {
+ setSearchAttempted(true);
+ }
+ };
+
+ fetchArticles();
}, [titleQuery, setSearchParams]);
+ // handle bookmark toggling with an article with its specified ID
+ const handleToggleBookmark = async (article_id) => {
+ const article = articles.find((a) => a.id === article_id);
+ if (!article) return;
+
+ const toggleBookmark = bookmarkTogglerCreator(article_id);
+ await updateBookmark(article_id, article.isBookmarked, toggleBookmark);
+ };
+
+ // Handle errors from the bookmark hook
+ useEffect(() => {
+ if (error) {
+ toast.error(error);
+ }
+ }, [error]);
+
return (
-
-
-
-
- Displaying results for{" "}
- "{titleQuery}"
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+ Displaying results for{" "}
+
+ "{titleQuery}"
+
+
+
+ {articles &&
+ articles.length > 0 &&
+ articles.map((article) => (
+
handleToggleBookmark(article.id)}
+ />
+ ))}
+
+ {(!articles || articles.length === 0) && searchAttempted && (
+
+ Your query did not match any results
+
+ )}
+
+ {!searchAttempted && (
+
+ )}
+
+
+
+
+
+
+
+ >
);
}
diff --git a/frontend/src/pages/ArticleResultsPage/ArticleResultsPage.scss b/frontend/src/pages/ArticleResultsPage/ArticleResultsPage.scss
index d5ed740..1f7774f 100644
--- a/frontend/src/pages/ArticleResultsPage/ArticleResultsPage.scss
+++ b/frontend/src/pages/ArticleResultsPage/ArticleResultsPage.scss
@@ -13,6 +13,12 @@
}
}
+.no-article-results {
+ @include media-breakpoint-up(lg) {
+ padding-left: 20px;
+ }
+}
+
.article-results-title-query {
color: $primary;
}
@@ -31,7 +37,8 @@
}
@include color-mode(light) {
- .article-results-title {
+ .article-results-title,
+ .no-article-results {
color: $primary-text-light;
}
.side-sections-container {
@@ -39,7 +46,8 @@
}
}
@include color-mode(dark) {
- .article-results-title {
+ .article-results-title,
+ .no-article-results {
color: $primary-text-dark;
}
.side-sections-container {
diff --git a/frontend/src/pages/ArticleView.jsx b/frontend/src/pages/ArticleView.jsx
index 4bcc953..0b5e4ee 100644
--- a/frontend/src/pages/ArticleView.jsx
+++ b/frontend/src/pages/ArticleView.jsx
@@ -1,16 +1,28 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import Article from "../Components/Article/Article.jsx";
+import { useAuthContext } from "../hooks/useAuthContext.jsx";
export default function ArticleView() {
const [article, setArticle] = useState();
const { name = "" } = useParams();
useEffect(() => {
- fetch(`http://localhost:3002/api/articles/${name}`)
- .then((res) => res.json()
- .then((data) => {
+ const authUser = localStorage.getItem("user");
+
+ const user = (authUser ? JSON.parse(authUser):undefined);
+
+ fetch(`http://localhost:3002/api/articles/${name}`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: (user ? `Bearer ${user.token}` : undefined),
+ },
+ })
+ .then((res) =>
+ res.json().then((data) => {
let article = data[0];
+ article.isBookmarked = (article.isBookmarked ? article.isBookmarked : false);
console.log(data);
setArticle(article);
})
diff --git a/frontend/src/pages/HomePage/Home.jsx b/frontend/src/pages/HomePage/Home.jsx
index 358d6da..ee2842d 100644
--- a/frontend/src/pages/HomePage/Home.jsx
+++ b/frontend/src/pages/HomePage/Home.jsx
@@ -14,7 +14,6 @@ import devin_ai from "../../assets/Article_Images/devin_ai.png";
import ai_in_business from "../../assets/Article_Images/ai_in_business.png";
import quantum from "../../assets/Article_Images/quantum.png";
import ai_brain from "../../assets/ml_brain.jpg";
-
export default function Home() {
// demo feature article
const feature_article_example = {
@@ -74,8 +73,11 @@ export default function Home() {
},
];
+
+
// useEffect to fetch the main feature article from the database (added later)
- useEffect(() => {}, []);
+ useEffect(() => {
+ }, []);
return (
<>
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index 081c8d9..a93e4a2 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -1,6 +1,17 @@
-import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
+import { defineConfig, loadEnv } from 'vite';
+import react from '@vitejs/plugin-react';
-export default defineConfig({
- plugins: [react()],
-});
+export default ({ mode }) => {
+ // Load environment variables based on the current mode
+ const env = loadEnv(mode, process.cwd());
+
+ return defineConfig({
+ plugins: [react()],
+ define: {
+ // Make sure to stringify the environment variables
+ 'process.env': {
+ VITE_API_URL: JSON.stringify(env.VITE_API_URL),
+ },
+ },
+ });
+};
diff --git a/package-lock.json b/package-lock.json
index a0dd4dd..8fb2d74 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,10 +10,17 @@
"license": "ISC",
"dependencies": {
"@vitejs/plugin-react": "^4.2.1",
+ "cloudinary": "^2.0.3",
"cors": "^2.8.5",
+ "dotenv": "^16.4.5",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.2",
"man": "^2.0.0",
+ "modules": "^0.4.0",
+ "multer": "^1.4.5-lts.1",
+ "react-bootstrap-typeahead": "^6.3.2",
+ "react-hot-toast": "^2.4.1",
+ "react-icons": "^5.2.1",
"vite": "^5.0.12"
},
"devDependencies": {
@@ -437,7 +444,6 @@
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
"integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
- "dev": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@@ -879,6 +885,15 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@remix-run/router": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.11.0.tgz",
@@ -888,6 +903,17 @@
"node": ">=14.0.0"
}
},
+ "node_modules/@restart/hooks": {
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
+ "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.9.6",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz",
@@ -1086,6 +1112,25 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
},
+ "node_modules/@types/prop-types": {
+ "version": "15.7.12",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.3",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
+ "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/warning": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
+ "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q=="
+ },
"node_modules/@vitejs/plugin-react": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz",
@@ -1159,6 +1204,11 @@
"node": ">= 8"
}
},
+ "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/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -1273,6 +1323,22 @@
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
},
+ "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",
@@ -1368,6 +1434,11 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
+ },
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -1382,6 +1453,18 @@
"node": ">=12"
}
},
+ "node_modules/cloudinary": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.0.3.tgz",
+ "integrity": "sha512-2JPxAUuV4iHwiW4ATSOZvii6+BhhKI9+9KscgUkxJPKa6V6wOnZJHlYyovBGrrIbIgEdmGSZgqEsLfD0wWBhBg==",
+ "dependencies": {
+ "lodash": "^4.17.21",
+ "q": "^1.5.1"
+ },
+ "engines": {
+ "node": ">=9"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -1400,12 +1483,31 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/compute-scroll-into-view": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz",
+ "integrity": "sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg=="
+ },
"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/concurrently": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz",
@@ -1470,6 +1572,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",
@@ -1482,6 +1589,11 @@
"node": ">= 0.10"
}
},
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
"node_modules/date-fns": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
@@ -1540,6 +1652,14 @@
"node": ">= 0.8"
}
},
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -1549,6 +1669,26 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -1701,6 +1841,11 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -1831,6 +1976,14 @@
"node": ">=4"
}
},
+ "node_modules/goober": {
+ "version": "2.1.14",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz",
+ "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
+ }
+ },
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@@ -1938,6 +2091,14 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -1997,6 +2158,11 @@
"node": ">=0.12.0"
}
},
+ "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/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -2067,8 +2233,12 @@
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
- "dev": true
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"node_modules/lodash.includes": {
"version": "4.3.0",
@@ -2109,8 +2279,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
- "peer": true,
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
@@ -2203,11 +2371,55 @@
"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/modules": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/modules/-/modules-0.4.0.tgz",
+ "integrity": "sha512-LX4JgwPHJr1FurPDKp1BlGgMXqZXtxO1O8ABGmj2g15CbLGlInTHcA9flqw6uN6oYKE2T0ngWdiHvcX97mdBsw==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
+ "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",
@@ -2395,6 +2607,21 @@
"node": "^10 || ^12 || >=14"
}
},
+ "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",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -2413,6 +2640,15 @@
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
+ "node_modules/q": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+ "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==",
+ "engines": {
+ "node": ">=0.6.0",
+ "teleport": ">=0.2.0"
+ }
+ },
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -2453,7 +2689,6 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
- "dev": true,
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
@@ -2462,11 +2697,36 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-bootstrap-typeahead": {
+ "version": "6.3.2",
+ "resolved": "https://registry.npmjs.org/react-bootstrap-typeahead/-/react-bootstrap-typeahead-6.3.2.tgz",
+ "integrity": "sha512-N5Mb0WlSSMcD7Z0pcCypILgIuECybev0hl4lsnCa5lbXTnN4QdkuHLGuTLSlXBwm1ZMFpOc2SnsdSRgeFiF+Ow==",
+ "dependencies": {
+ "@babel/runtime": "^7.14.6",
+ "@popperjs/core": "^2.10.2",
+ "@restart/hooks": "^0.4.0",
+ "classnames": "^2.2.0",
+ "fast-deep-equal": "^3.1.1",
+ "invariant": "^2.2.1",
+ "lodash.debounce": "^4.0.8",
+ "prop-types": "^15.5.8",
+ "react-overlays": "^5.2.0",
+ "react-popper": "^2.2.5",
+ "scroll-into-view-if-needed": "^3.1.0",
+ "warning": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
- "dev": true,
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
@@ -2476,6 +2736,77 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
+ },
+ "node_modules/react-hot-toast": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
+ "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==",
+ "dependencies": {
+ "goober": "^2.1.10"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
+ "node_modules/react-icons": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz",
+ "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "node_modules/react-overlays": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz",
+ "integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.8",
+ "@popperjs/core": "^2.11.6",
+ "@restart/hooks": "^0.4.7",
+ "@types/warning": "^3.0.0",
+ "dom-helpers": "^5.2.0",
+ "prop-types": "^15.7.2",
+ "uncontrollable": "^7.2.1",
+ "warning": "^4.0.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.3.0",
+ "react-dom": ">=16.3.0"
+ }
+ },
+ "node_modules/react-popper": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
+ "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+ "dependencies": {
+ "react-fast-compare": "^3.0.1",
+ "warning": "^4.0.2"
+ },
+ "peerDependencies": {
+ "@popperjs/core": "^2.0.0",
+ "react": "^16.8.0 || ^17 || ^18",
+ "react-dom": "^16.8.0 || ^17 || ^18"
+ }
+ },
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@@ -2516,6 +2847,25 @@
"react-dom": ">=16.8"
}
},
+ "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/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -2531,8 +2881,7 @@
"node_modules/regenerator-runtime": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
- "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==",
- "dev": true
+ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
},
"node_modules/require-directory": {
"version": "2.1.1",
@@ -2628,12 +2977,19 @@
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
- "dev": true,
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
}
},
+ "node_modules/scroll-into-view-if-needed": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz",
+ "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==",
+ "dependencies": {
+ "compute-scroll-into-view": "^3.0.2"
+ }
+ },
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
@@ -2773,6 +3129,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",
@@ -2881,6 +3258,25 @@
"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/uncontrollable": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
+ "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.6.3",
+ "@types/react": ">=16.9.11",
+ "invariant": "^2.2.4",
+ "react-lifecycles-compat": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react": ">=15.0.0"
+ }
+ },
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
@@ -2924,6 +3320,11 @@
"browserslist": ">= 4.21.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",
@@ -2994,6 +3395,14 @@
}
}
},
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -3011,6 +3420,14 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "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",
@@ -3364,7 +3781,6 @@
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
"integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
- "dev": true,
"requires": {
"regenerator-runtime": "^0.14.0"
}
@@ -3578,12 +3994,25 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
+ },
"@remix-run/router": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.11.0.tgz",
"integrity": "sha512-BHdhcWgeiudl91HvVa2wxqZjSHbheSgIiDvxrF1VjFzBzpTtuDPkOdOi3Iqvc08kXtFkLjhbS+ML9aM8mJS+wQ==",
"dev": true
},
+ "@restart/hooks": {
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
+ "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==",
+ "requires": {
+ "dequal": "^2.0.3"
+ }
+ },
"@rollup/rollup-android-arm-eabi": {
"version": "4.9.6",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz",
@@ -3704,6 +4133,25 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
},
+ "@types/prop-types": {
+ "version": "15.7.12",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
+ },
+ "@types/react": {
+ "version": "18.3.3",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
+ "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
+ "requires": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@types/warning": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
+ "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q=="
+ },
"@vitejs/plugin-react": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz",
@@ -3756,6 +4204,11 @@
"picomatch": "^2.0.4"
}
},
+ "append-field": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
+ },
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -3842,6 +4295,19 @@
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
},
+ "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=="
+ },
+ "busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "requires": {
+ "streamsearch": "^1.1.0"
+ }
+ },
"bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -3899,6 +4365,11 @@
"readdirp": "~3.6.0"
}
},
+ "classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
+ },
"cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -3910,6 +4381,15 @@
"wrap-ansi": "^7.0.0"
}
},
+ "cloudinary": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.0.3.tgz",
+ "integrity": "sha512-2JPxAUuV4iHwiW4ATSOZvii6+BhhKI9+9KscgUkxJPKa6V6wOnZJHlYyovBGrrIbIgEdmGSZgqEsLfD0wWBhBg==",
+ "requires": {
+ "lodash": "^4.17.21",
+ "q": "^1.5.1"
+ }
+ },
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -3925,12 +4405,28 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "compute-scroll-into-view": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz",
+ "integrity": "sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg=="
+ },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
"concurrently": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz",
@@ -3976,6 +4472,11 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
+ "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=="
+ },
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
@@ -3985,6 +4486,11 @@
"vary": "^1"
}
},
+ "csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
"date-fns": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
@@ -4024,11 +4530,30 @@
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
+ "dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="
+ },
"destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
},
+ "dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "requires": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="
+ },
"ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -4161,6 +4686,11 @@
}
}
},
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -4256,6 +4786,12 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
},
+ "goober": {
+ "version": "2.1.14",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz",
+ "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==",
+ "requires": {}
+ },
"gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@@ -4333,6 +4869,14 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -4374,6 +4918,11 @@
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"devOptional": true
},
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -4428,8 +4977,12 @@
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
- "dev": true
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"lodash.includes": {
"version": "4.3.0",
@@ -4470,8 +5023,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
- "peer": true,
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
@@ -4531,11 +5082,43 @@
"brace-expansion": "^1.1.7"
}
},
+ "minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
+ },
+ "mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "requires": {
+ "minimist": "^1.2.6"
+ }
+ },
+ "modules": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/modules/-/modules-0.4.0.tgz",
+ "integrity": "sha512-LX4JgwPHJr1FurPDKp1BlGgMXqZXtxO1O8ABGmj2g15CbLGlInTHcA9flqw6uN6oYKE2T0ngWdiHvcX97mdBsw=="
+ },
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
+ "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==",
+ "requires": {
+ "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"
+ }
+ },
"nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@@ -4650,6 +5233,21 @@
"source-map-js": "^1.0.2"
}
},
+ "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=="
+ },
+ "prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
"proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -4665,6 +5263,11 @@
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
+ "q": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+ "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw=="
+ },
"qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -4693,23 +5296,93 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
- "dev": true,
"peer": true,
"requires": {
"loose-envify": "^1.1.0"
}
},
+ "react-bootstrap-typeahead": {
+ "version": "6.3.2",
+ "resolved": "https://registry.npmjs.org/react-bootstrap-typeahead/-/react-bootstrap-typeahead-6.3.2.tgz",
+ "integrity": "sha512-N5Mb0WlSSMcD7Z0pcCypILgIuECybev0hl4lsnCa5lbXTnN4QdkuHLGuTLSlXBwm1ZMFpOc2SnsdSRgeFiF+Ow==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "@popperjs/core": "^2.10.2",
+ "@restart/hooks": "^0.4.0",
+ "classnames": "^2.2.0",
+ "fast-deep-equal": "^3.1.1",
+ "invariant": "^2.2.1",
+ "lodash.debounce": "^4.0.8",
+ "prop-types": "^15.5.8",
+ "react-overlays": "^5.2.0",
+ "react-popper": "^2.2.5",
+ "scroll-into-view-if-needed": "^3.1.0",
+ "warning": "^4.0.1"
+ }
+ },
"react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
- "dev": true,
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
}
},
+ "react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
+ },
+ "react-hot-toast": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
+ "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==",
+ "requires": {
+ "goober": "^2.1.10"
+ }
+ },
+ "react-icons": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz",
+ "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==",
+ "requires": {}
+ },
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "react-overlays": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz",
+ "integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==",
+ "requires": {
+ "@babel/runtime": "^7.13.8",
+ "@popperjs/core": "^2.11.6",
+ "@restart/hooks": "^0.4.7",
+ "@types/warning": "^3.0.0",
+ "dom-helpers": "^5.2.0",
+ "prop-types": "^15.7.2",
+ "uncontrollable": "^7.2.1",
+ "warning": "^4.0.3"
+ }
+ },
+ "react-popper": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
+ "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+ "requires": {
+ "react-fast-compare": "^3.0.1",
+ "warning": "^4.0.2"
+ }
+ },
"react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@@ -4734,6 +5407,27 @@
"react-router": "6.18.0"
}
},
+ "readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "requires": {
+ "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"
+ },
+ "dependencies": {
+ "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=="
+ }
+ }
+ },
"readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -4746,8 +5440,7 @@
"regenerator-runtime": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
- "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==",
- "dev": true
+ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
},
"require-directory": {
"version": "2.1.1",
@@ -4811,12 +5504,19 @@
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
- "dev": true,
"peer": true,
"requires": {
"loose-envify": "^1.1.0"
}
},
+ "scroll-into-view-if-needed": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz",
+ "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==",
+ "requires": {
+ "compute-scroll-into-view": "^3.0.2"
+ }
+ },
"semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
@@ -4930,6 +5630,26 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
},
+ "streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
+ },
+ "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==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ },
+ "dependencies": {
+ "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=="
+ }
+ }
+ },
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@@ -5008,6 +5728,22 @@
"mime-types": "~2.1.24"
}
},
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
+ },
+ "uncontrollable": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
+ "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
+ "requires": {
+ "@babel/runtime": "^7.6.3",
+ "@types/react": ">=16.9.11",
+ "invariant": "^2.2.4",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
"undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
@@ -5028,6 +5764,11 @@
"picocolors": "^1.0.0"
}
},
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -5049,6 +5790,14 @@
"rollup": "^4.2.0"
}
},
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -5060,6 +5809,11 @@
"strip-ansi": "^6.0.0"
}
},
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+ },
"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 78768df..21e5f0f 100644
--- a/package.json
+++ b/package.json
@@ -28,10 +28,17 @@
},
"dependencies": {
"@vitejs/plugin-react": "^4.2.1",
+ "cloudinary": "^2.0.3",
"cors": "^2.8.5",
+ "dotenv": "^16.4.5",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.2",
"man": "^2.0.0",
+ "modules": "^0.4.0",
+ "multer": "^1.4.5-lts.1",
+ "react-bootstrap-typeahead": "^6.3.2",
+ "react-hot-toast": "^2.4.1",
+ "react-icons": "^5.2.1",
"vite": "^5.0.12"
}
}
diff --git a/server/db.js b/server/db.js
index 7541cf7..6f76e93 100644
--- a/server/db.js
+++ b/server/db.js
@@ -9,7 +9,7 @@ const pool = new Pool(
host: process.env.PGHOST,
database: process.env.PGDATABASE,
password: process.env.PGPASSWORD,
- port: process.env.PGPORT,
+ port: process.env.PGPORT,
}
)
diff --git a/server/index.js b/server/index.js
index 3fd3e4e..42ddbdb 100644
--- a/server/index.js
+++ b/server/index.js
@@ -6,13 +6,18 @@ const userRoutes = require("./src/users/routes.js");
const auth_userRoutes = require("./src/users/authroutes.js");
const bookmarkRoutes = require("./src/bookmarks/authroutes.js")
+const imagesRoutes = require("./src/images/routes.js");
const cors = require("cors");
require("dotenv").config();
+console.log(process.env.PGUSER);
+
const app = express();
+// const number = 8080
const port = process.env.PORT;
+// const port1= 8080
app.use(cors());
app.use(express.json());
@@ -31,5 +36,6 @@ app.use("/api/courses", courseRoutes);
app.use('/api/users', userRoutes);
app.use('/api/users', auth_userRoutes);
+app.use("/api/images", imagesRoutes); // images routes
-app.listen(port, () => console.log(`app listening on port ${port}`));
+app.listen(port, () => console.log(`app listening on port ${port}`));
\ No newline at end of file
diff --git a/server/package-lock.json b/server/package-lock.json
index 10864c4..0263623 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -16,6 +16,7 @@
"express": "^4.18.2",
"firebase-admin": "^12.0.0",
"jsonwebtoken": "^9.0.2",
+ "modules": "^0.4.0",
"oauth": "^0.10.0",
"pg": "^8.11.3"
}
@@ -1893,6 +1894,14 @@
"node": ">=10"
}
},
+ "node_modules/modules": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/modules/-/modules-0.4.0.tgz",
+ "integrity": "sha512-LX4JgwPHJr1FurPDKp1BlGgMXqZXtxO1O8ABGmj2g15CbLGlInTHcA9flqw6uN6oYKE2T0ngWdiHvcX97mdBsw==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -4281,6 +4290,11 @@
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
},
+ "modules": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/modules/-/modules-0.4.0.tgz",
+ "integrity": "sha512-LX4JgwPHJr1FurPDKp1BlGgMXqZXtxO1O8ABGmj2g15CbLGlInTHcA9flqw6uN6oYKE2T0ngWdiHvcX97mdBsw=="
+ },
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
diff --git a/server/package.json b/server/package.json
index 10d1d7c..ef7459a 100644
--- a/server/package.json
+++ b/server/package.json
@@ -16,6 +16,7 @@
"express": "^4.18.2",
"firebase-admin": "^12.0.0",
"jsonwebtoken": "^9.0.2",
+ "modules": "^0.4.0",
"oauth": "^0.10.0",
"pg": "^8.11.3"
}
diff --git a/server/src/articles/controller.js b/server/src/articles/controller.js
index f9e3d3c..11a572a 100644
--- a/server/src/articles/controller.js
+++ b/server/src/articles/controller.js
@@ -4,6 +4,7 @@ const queries = require("./queries");
const getArticles = async (req, res) => {
console.log("GET ARTICLES");
if (req.user) {
+ console.log("WITH USER")
if (req.query.title) {
const articles = await pool.query(queries.auth_getArticlesByTitle.replace('$1',req.query.title).replace('$2',req.user.id),(error, results) => {
if (error) {
@@ -22,6 +23,7 @@ const getArticles = async (req, res) => {
});
}
} else {
+ console.log("WITHOUT USER")
if (req.query.title) {
pool.query(queries.getArticlesByTitle.replace('$1',req.query.title),(error, results) => {
if (error) {
@@ -70,7 +72,6 @@ const addArticles = (req, res) => {
const { title,headers,author_id } = req.body;
let headers_json = JSON.stringify(headers);
-
pool.query(queries.addArticles, [title, author_id,headers_json], (error, results) => {
if (error) {
console.error(error);
diff --git a/server/src/articles/routes.js b/server/src/articles/routes.js
index 73c9d28..9861f9e 100644
--- a/server/src/articles/routes.js
+++ b/server/src/articles/routes.js
@@ -1,7 +1,7 @@
const { Router } = require("express");
const controller = require("./controller");
-//const authorizeArticle = require('../middleware/authorizeArticle')
+const authorizeArticle = require('../middleware/authorizeArticle')
const optionalAuth = require('../middleware/optionalAuth');
const router = Router();
@@ -12,4 +12,7 @@ router.use(optionalAuth);
router.get("/", async (req, res) => controller.getArticles(req, res));
router.get("/:id", (req, res) => controller.getArticlesById(req, res));
+router.post("/", (req, res) => controller.addArticles(req, res));
+router.delete('/:id', authorizeArticle, controller.deleteArticle);
+
module.exports = router;
diff --git a/server/src/images/config.js b/server/src/images/config.js
new file mode 100644
index 0000000..3e7e7be
--- /dev/null
+++ b/server/src/images/config.js
@@ -0,0 +1,18 @@
+const cloudinary = require('cloudinary').v2;
+require("dotenv").config();
+// export const cloudinary1 = cloudinary
+// transit the data into .env
+cloudinary.config({
+ cloud_name: pocess.env.CLOUD_NAME,
+ api_key: process.env.API_KEY,
+ api_secret: process.env.API_SECRET
+});
+
+
+process.env.PGUSER,
+
+module.exports = {
+ cloudinary1:cloudinary
+}
+
+
diff --git a/server/src/images/controller.js b/server/src/images/controller.js
new file mode 100644
index 0000000..9d13cd1
--- /dev/null
+++ b/server/src/images/controller.js
@@ -0,0 +1,103 @@
+const { createSearchParams } = require("react-router-dom")
+const { cloudinary1 } = require("./config");
+const { upload } = require("./multer");
+
+const getImages = async (req, res) => {
+ try {
+ const images = await cloudinary1.api.resources();
+ res.json(images);
+ console.log(images);
+ }
+ catch (err) {
+ console.error(err);
+ res.status(500).message(err.message);
+ }
+}
+
+const getImagesById = async (req, res) => {
+ const {id} = req.params;
+ try {
+ const image = await cloudinary1.api.resource(id);
+ res.json(image);
+ }
+ catch (err) {
+ console.log(err);
+ res.status(404).message(err.message);
+ }
+
+}
+
+const postImage = async (req, res) => {
+ try {
+ // Use multer's single() middleware to handle single file upload with field name 'image'
+ upload.single('image')(req, res, async (err) => {
+ if (err) {
+ console.error(err);
+ return res.status(400).json({
+ success: false,
+ message: 'Error uploading image'
+ });
+ }
+
+ // If multer successfully parsed the file, you can proceed with Cloudinary upload
+ try {
+ const result = await cloudinary1.uploader.upload(req.file.path);
+ res.status(200).json({
+ success: true,
+ message: 'Image uploaded successfully',
+ data: result // Optionally, you can send uploaded image data back to the client
+ });
+ } catch (uploadError) {
+ console.error(uploadError);
+ res.status(500).json({
+ success: false,
+ message: 'Error uploading image to Cloudinary'
+ });
+ }
+ });
+ } catch (multerError) {
+ console.error(multerError);
+ res.status(500).json({
+ success: false,
+ message: 'Error processing file upload'
+ });
+ }
+};
+const deleteImageById = async (req, res) => {
+ const {id} = req.params;
+ try {
+ const image = await cloudinary1.api.resource(id);
+ if (!image) {
+ res.status(404).json({
+ message: 'Image not found'
+ })
+ }
+ else {
+ await cloudinary1.api.delete_resources(id)
+ .then(resp => {
+ console.log(resp);
+ res.status(200).json({
+ message: "the image was successfully deleted",
+ });
+ })
+ .catch(err => {
+ console.log(err);
+ res.status(500).json({ message: "there has been an error deleting the image", })
+
+ });
+ }
+ }
+ catch (err) {
+ console.error(err);
+ res.status(500).json({ message: err.message });
+
+ }
+}
+
+
+module.exports = {
+ getImages,
+ getImagesById,
+ postImage,
+ deleteImageById
+}
\ No newline at end of file
diff --git a/server/src/images/multer.js b/server/src/images/multer.js
new file mode 100644
index 0000000..2544c91
--- /dev/null
+++ b/server/src/images/multer.js
@@ -0,0 +1,12 @@
+const multer = require ("multer")
+// multer middleware to handle files
+const storage = multer.diskStorage({
+ filename: function(req,file,cb){
+ cb(null, file.originalname)
+ }
+});
+// export const upload = multer({storage: storage});
+
+module.exports = {
+ upload:multer({storage: storage})
+}
\ No newline at end of file
diff --git a/server/src/images/routes.js b/server/src/images/routes.js
new file mode 100644
index 0000000..2dc4160
--- /dev/null
+++ b/server/src/images/routes.js
@@ -0,0 +1,11 @@
+const {Router} = require("express")
+const controller = require("./controller");
+const upload = require("multer")
+const router = new Router();
+
+router.get("/" , controller.getImages);
+router.get("/:id", controller.getImagesById);
+router.post("/" , controller.postImage);
+router.delete("/:id", controller.deleteImageById);
+
+module.exports = router;
\ No newline at end of file
diff --git a/server/src/users/controller.js b/server/src/users/controller.js
index 06d7c84..18ff695 100644
--- a/server/src/users/controller.js
+++ b/server/src/users/controller.js
@@ -39,7 +39,15 @@ const createUser = async (req, res) => {
const salt = await bcrypt.genSalt()
const hashedPassword = await bcrypt.hash(req.body.user_password, salt)
const { username } = req.body;
- pool.query(queries.createUser, [username, hashedPassword])
+ pool.query(queries.createUser, [username, hashedPassword], (err, results) => {
+ if(err){
+ console.error(err);
+ return res.status(500).json({
+
+ })
+ }
+
+ })
const token = createToken(username);