Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…ssessment into ZP/delete-videos-endpoints
  • Loading branch information
zelihapala committed May 26, 2024
2 parents 574e88d + 5fad9d6 commit 9f3e408
Show file tree
Hide file tree
Showing 12 changed files with 377 additions and 16 deletions.
3 changes: 2 additions & 1 deletion client/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"react-refresh/only-export-components": [
"warn",
{ "allowConstantExport": true }
]
],
"react/prop-types": 0
}
}
11 changes: 11 additions & 0 deletions client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,18 @@
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Video Recommendations</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/index.jsx"></script>
Expand Down
3 changes: 3 additions & 0 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import VideoList from "./VideoRecommendations";

const App = () => {
return (
<>
<h1>Video Recommendations</h1>
<VideoList />
</>
);
};
Expand Down
33 changes: 33 additions & 0 deletions client/src/DeleteVideoRecommendation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from "react";

const DeleteVideoRecommendation = ({ videoId, onDelete }) => {
const handleDelete = () => {
fetch(`/api/videos/${videoId}`, {
method: "DELETE",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
})
.then((response) => {
if (!response.ok) {
throw new Error("Failed to delete the video");
}
return response.json();
})
.then((data) => {
onDelete(videoId);
})
.catch((error) => {
console.error(error);
});
};

return (
<button className="submit-button" onClick={handleDelete}>
Remove Video
</button>
);
};

export default DeleteVideoRecommendation;
46 changes: 46 additions & 0 deletions client/src/NewVideoForm.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.form-container {
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
max-width: 400px;
margin: auto;
margin-top: 30px;
}

.form-header {
text-align: center;
margin-bottom: 20px;
}

.form-body {
display: flex;
flex-direction: column;
}

.input-group {
margin-bottom: 15px;
}

.input-group label {
margin-bottom: 5px;
display: block;
}

.input-group input {
width: 100%;
padding: 8px;
box-sizing: border-box;
}

.submit-button {
padding: 10px 15px;
background-color: #003b7a;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
}

.submit-button:hover {
background-color: #f5ba13;
}
63 changes: 63 additions & 0 deletions client/src/NewVideoForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useState } from "react";
import "./NewVideoForm.css";

const NewVideoForm = ({ onSubmit }) => {
const [title, setTitle] = useState("");
const [src, setSrc] = useState("");

const handleSubmit = (e) => {
e.preventDefault();

fetch("/api/videos", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ title, src }),
})
.then((response) => response.json())
.then((data) => {
console.log(JSON.stringify(data));
})
.catch((error) => {
console.error(error);
});
onSubmit();
setTitle("");
setSrc("");
};

return (
<div className="form-container">
<h2 className="form-header">Add New Video</h2>
<form className="form-body" onSubmit={handleSubmit}>
<div className="input-group">
<label htmlFor="title">Video Title: </label>
<input
type="text"
id="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
required
/>
</div>
<div className="input-group">
<label htmlFor="src">Video URL: </label>
<input
type="url"
id="src"
value={src}
onChange={(e) => setSrc(e.target.value)}
required
/>
</div>
<button className="submit-button" type="submit">
Submit
</button>
</form>
</div>
);
};

export default NewVideoForm;
43 changes: 43 additions & 0 deletions client/src/RatingDisplay.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const RatingDisplay = ({ videoId, rating, onUpdate }) => {
const handleRating = (updatedRating) => {
fetch(`/api/videos/${videoId}/rating`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},

body: JSON.stringify({
rating: updatedRating,
}),
})
.then((response) => {
if (!response.ok) {
throw new Error("Failed to update the video rating");
}
return response.json();
})
.then((data) => {
onUpdate(videoId, data.data.rating);
})
.catch((error) => {
console.error(error);
});
};

return (
<div>
<div>{rating}</div>
<div>
<button onClick={() => handleRating(rating + 1)}>
Thumbs-up! <i className="fa fa-thumbs-up"></i>
</button>
<button onClick={() => handleRating(rating - 1)}>
Thumbs-down! <i className="fa fa-thumbs-down"></i>
</button>
</div>
</div>
);
};

export default RatingDisplay;
44 changes: 44 additions & 0 deletions client/src/VideoRecommendations.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
:root {
--primary-color: #007bff;
--container-width: 90%;
--gap-size: 20px;
}

.video-list-container {
padding: 20px;
margin: 0 auto;
width: var(--container-width);
}

.video-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--gap-size);
}

.video-item {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
}

.video-title {
font-size: 18px;
margin-bottom: 10px;
font-family: Arial, sans-serif;
}

.video-item iframe {
width: 100%;
height: 200px; /* Adjust height of each video */
border: none;
}

.video-title a {
color: var(--primary-color);
}

.video-title a:hover {
text-decoration: underline;
}
83 changes: 83 additions & 0 deletions client/src/VideoRecommendations.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useState, useEffect } from "react";
import "./VideoRecommendations.css";
import DeleteVideoRecommendation from "./DeleteVideoRecommendation";
import NewVideoForm from "./NewVideoForm.jsx";
import RatingDisplay from "./RatingDisplay.jsx";
const VideoList = () => {
const [videos, setVideos] = useState([]);

function fetchVideos() {
fetch("/api/videos", {
method: "GET",
headers: {
Accept: "application/json",
},
})
.then((response) => response.json())
.then((data) => {
setVideos(data.sort((a, b) => a.id - b.id));
})
.catch((error) => {
console.error(error);
});
}

useEffect(() => {
fetchVideos();
}, []);

const handleDelete = (videoId) => {
setVideos(videos.filter((video) => video.id !== videoId));
};

const handleRatingUpdate = (videoId, rating) => {
const updatedVideos = videos.map((video) =>
video.id === videoId ? { ...video, rating: rating } : video
);
setVideos(updatedVideos);
};

function changeYTLinkToEmbed(watchLink) {
const embedLink = watchLink.replace("watch?v=", "embed/");
return embedLink;
}

return (
<div className="video-list-container">
<div className="video-list">
{videos.map((videoData, i) => {
const embededLink = changeYTLinkToEmbed(videoData.src);
return (
<div className="video-item" data-testid="video" key={i}>
<div className="video-title">
<a href={videoData.src}>{videoData.title}</a>
</div>
<div>
<iframe
title={videoData.title}
width="560"
height="315"
src={embededLink}
frameBorder="0"
allowFullScreen
></iframe>
</div>
<DeleteVideoRecommendation
videoId={videoData.id}
onDelete={handleDelete}
/>
<RatingDisplay
videoId={videoData.id}
rating={videoData.rating}
onUpdate={handleRatingUpdate}
/>
</div>
);
})}
</div>
<NewVideoForm onSubmit={fetchVideos} />
</div>
);
};

export default VideoList;
18 changes: 14 additions & 4 deletions client/src/index.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
html,
body {
margin: 0;
}

h1 {
text-align: center;
color: #003b7a;
font-family: "Roboto", sans-serif;
font-weight: 700;
font-style: italic;
}

.app {
display: grid;
margin: auto;
margin-bottom: 7rem;
}

body {
background-image: url("https://www.transparenttextures.com/patterns/cubes.png");
}
Loading

0 comments on commit 9f3e408

Please sign in to comment.