diff --git a/client/src/App.jsx b/client/src/App.jsx
index 21656a5951..8031e2a983 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -1,10 +1,50 @@
import Videos from "./Videos/Videos";
+import { useState, useEffect, useRef } from "react";
+import VideoForm from "./VideoForm/VideoForm";
+import "./App.scss";
const App = () => {
+ const [videos, setVideos] = useState([]);
+ const [fetchedVideos, setFetchedVideos] = useState(false);
+
+ useEffect(() => {
+ fetch("/api/videos")
+ .then((res) => res.json())
+ .then((data) => {
+ setVideos(data);
+ });
+ setFetchedVideos(false);
+ }, [fetchedVideos]);
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ const formData = Object.fromEntries(new FormData(e.target));
+ const response = await fetch("/api/videos", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(formData),
+ });
+
+ if (response.ok) {
+ setFetchedVideos(true);
+
+ }
+
+ if (!response.ok) {
+ throw new Error("Failed to add video");
+ }
+
+ const responseData = await response.json();
+ e.target.reset();
+ };
+
return (
<>
{video.title}
@@ -30,7 +14,11 @@ const Videos = () => {
);
});
- return
{mapVideos}
;
+ return (
+ <>
+
{mapVideos}
+ >
+ );
};
export default Videos;
diff --git a/client/src/Videos/videos.scss b/client/src/Videos/videos.scss
index 6c00affe40..4d0aa95a53 100644
--- a/client/src/Videos/videos.scss
+++ b/client/src/Videos/videos.scss
@@ -1,6 +1,12 @@
.container {
margin-top: 2rem;
padding-left: 1rem;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ max-width: 25rem;
+ height: 15rem;
&_video {
border-radius: 1rem;
diff --git a/db/initdb.sql b/db/initdb.sql
index bb40b5058f..844d4b122c 100644
--- a/db/initdb.sql
+++ b/db/initdb.sql
@@ -2,16 +2,86 @@ DROP TABLE IF EXISTS videos CASCADE;
CREATE TABLE videos (
id SERIAL PRIMARY KEY,
- title VARCHAR,
- src VARCHAR);
-
-INSERT INTO videos (title,src) VALUES ('Never Gonna Give You Up','https://www.youtube.com/embed/dQw4w9WgXcQ?si=sdvqEritjOTwN2Af');
-INSERT INTO videos (title,src) VALUES ('The Coding Train','https://www.youtube.com/embed/HerCR8bw_GE?si=5Xfqx9K1JMB_QCBh');
-INSERT INTO videos (title,src) VALUES ('Mac & Cheese | Basics with Babish','https://www.youtube.com/embed/FUeyrEN14Rk?si=dUHtCerjTKIdgK5u');
-INSERT INTO videos (title,src) VALUES ('Videos for Cats to Watch - 8 Hour Bird Bonanza','https://www.youtube.com/embed/xbs7FT7dXYc?si=W9bjQcH1cYbIlnY3');
-INSERT INTO videos (title,src) VALUES ('The Complete London 2012 Opening Ceremony | London 2012 Olympic Games','https://www.youtube.com/embed/4As0e4de-rI?si=QvvaM7T6gj31cQ6z');
-INSERT INTO videos (title,src) VALUES ('Learn Unity - Beginners Game Development Course','https://www.youtube.com/embed/gB1F9G0JXOo?si=zh21-opwR7otFnSZ');
-INSERT INTO videos (title,src) VALUES ('Cracking Enigma in 2021 - Computerphile','https://www.youtube.com/embed/RzWB5jL5RX0?si=OuYo20zJalIFBT2w');
-INSERT INTO videos (title,src) VALUES ('Coding Adventure: Chess AI','https://www.youtube.com/embed/U4ogK0MIzqk?si=xICbZlD8Hm9nCyWy');
-INSERT INTO videos (title,src) VALUES ('Coding Adventure: Ant and Slime Simulations','https://www.youtube.com/embed/X-iSQQgOd1A?si=bZUPXmKxC43YzERA');
-INSERT INTO videos (title,src) VALUES ('Why the Tour de France is so brutal','https://www.youtube.com/embed/ZacOS8NBK6U?si=nfaj6AHw0aaE-c7C');
\ No newline at end of file
+ title VARCHAR,
+ src VARCHAR
+);
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'Never Gonna Give You Up',
+ 'https://www.youtube.com/embed/dQw4w9WgXcQ?si=sdvqEritjOTwN2Af'
+ );
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'The Coding Train',
+ 'https://www.youtube.com/embed/HerCR8bw_GE?si=5Xfqx9K1JMB_QCBh'
+ );
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'Mac & Cheese | Basics with Babish',
+ 'https://www.youtube.com/embed/FUeyrEN14Rk?si=dUHtCerjTKIdgK5u'
+ );
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'Videos for Cats to Watch - 8 Hour Bird Bonanza',
+ 'https://www.youtube.com/embed/xbs7FT7dXYc?si=W9bjQcH1cYbIlnY3'
+ );
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'The Complete London 2012 Opening Ceremony | London 2012 Olympic Games',
+ 'https://www.youtube.com/embed/4As0e4de-rI?si=QvvaM7T6gj31cQ6z'
+ );
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'Learn Unity - Beginners Game Development Course',
+ 'https://www.youtube.com/embed/gB1F9G0JXOo?si=zh21-opwR7otFnSZ'
+ );
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'Cracking Enigma in 2021 - Computerphile',
+ 'https://www.youtube.com/embed/RzWB5jL5RX0?si=OuYo20zJalIFBT2w'
+ );
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'Coding Adventure: Chess AI',
+ 'https://www.youtube.com/embed/U4ogK0MIzqk?si=xICbZlD8Hm9nCyWy'
+ );
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'Coding Adventure: Ant and Slime Simulations',
+ 'https://www.youtube.com/embed/X-iSQQgOd1A?si=bZUPXmKxC43YzERA'
+ );
+
+INSERT INTO
+ videos (title, src)
+VALUES
+ (
+ 'Why the Tour de France is so brutal',
+ 'https://www.youtube.com/embed/ZacOS8NBK6U?si=nfaj6AHw0aaE-c7C'
+ );
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 6886da3202..e133f6d6bc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"e2e"
],
"dependencies": {
+ "react-hook-form": "^7.51.3",
"sass": "^1.75.0",
"serverless-http": "^3.2.0"
},
@@ -8970,6 +8971,21 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-hook-form": {
+ "version": "7.51.3",
+ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.3.tgz",
+ "integrity": "sha512-cvJ/wbHdhYx8aviSWh28w9ImjmVsb5Y05n1+FW786vEZQJV5STNM0pW6ujS+oiBecb0ARBxJFyAnXj9+GHXACQ==",
+ "engines": {
+ "node": ">=12.22.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-hook-form"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17 || ^18"
+ }
+ },
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
diff --git a/package.json b/package.json
index 5b7d4b4853..4893e5241b 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"npm": ">=10"
},
"dependencies": {
+ "react-hook-form": "^7.51.3",
"sass": "^1.75.0",
"serverless-http": "^3.2.0"
}
diff --git a/server/api.js b/server/api.js
index 38b523a9f7..2a423492d3 100644
--- a/server/api.js
+++ b/server/api.js
@@ -7,19 +7,24 @@ router.get("/videos", async (_, res) => {
result
? res.send(result.rows)
: res
- .status(500)
- .send({ success: "false", error: "Could not connect to database" });
+ .status(500)
+ .send({ success: "false", error: "Could not connect to database" });
});
router.post("/videos", async (req, res) => {
- const newVideo = await db.query(
- `INSERT INTO videos (title, src) VALUES ('${req.body.title}', '${req.body.src}')`
- );
- res.send(
- newVideo
- ? res.send({ success: "Video added successfully" })
- : res.send({ error: "Video could not be added" })
- );
+ try {
+ await db.query(`INSERT INTO videos (title, src) VALUES ($1, $2)`, [req.body.title, req.body.src])
+ res.send({
+ success: true,
+ message: `Video added successfully: ${req.body.title}, ${req.body.src}`
+ });
+ } catch (error) {
+ res.status(500).send({
+ success: false,
+ error: "Video could not be added"
+ });
+ }
});
+
export default router;