diff --git a/src/App.js b/src/App.js
index 206efa9..8712325 100644
--- a/src/App.js
+++ b/src/App.js
@@ -8,6 +8,10 @@ import Show from "./components/shows/Show";
import ShowsEditForm from "./components/shows/ShowsEditForm";
import ShowsIndex from "./components/shows/ShowsIndex";
import ShowsNewForm from "./components/shows/ShowsNewForm";
+import MoviesIndex from "./components/movies/MoviesIndex";
+import MoviesNewForm from "./components/movies/MoviesNewForm";
+import Movie from "./components/movies/Movie";
+import MoviesEditForm from "./components/movies/MoviesEditForm";
function App() {
return (
@@ -20,6 +24,10 @@ function App() {
} />
} />
} />
+ } />
+ } />
+ } />
+ } />
diff --git a/src/api/fetch.js b/src/api/fetch.js
index cfb057a..882098a 100644
--- a/src/api/fetch.js
+++ b/src/api/fetch.js
@@ -1,32 +1,78 @@
+const URL = process.env.REACT_APP_API_BASE_URL;
// Shows
// Create
export function createShow(show) {
- return;
+ const options ={
+ method: "POST",
+ body: JSON.stringify(show),
+ headers: { "Content-Type": "application/json"},
+ }
+ return fetch(`${URL}/shows/`, options).then((response) => response.json());
}
// Delete
export function destroyShow(id) {
- return;
+ const options = { method: "DELETE" };
+ return fetch(`${URL}/shows/${id}`, options);
}
// Index/Get all
export function getAllShows() {
- return;
+ return fetch(`${URL}/shows`).then((response) => response.json());
}
// Show/Get one
export function getOneShow(id) {
- return;
+ return fetch(`${URL}/shows/${id}`).then((response) => response.json());
}
// Update
export function updateShow(id, show) {
- return;
+ const options = {
+ method: "PUT",
+ body: JSON.stringify(show),
+ headers: { "Content-Type": "application/json" },
+ }
+ return fetch(`${URL}/shows/${id}`, options).then(response => response.json());
}
+
+
// Movies
+// Create
+export function createMovie(movie) {
+ const options = {
+ method: "POST",
+ body: JSON.stringify(movie),
+ headers: { "Content-Type": "application/json" },
+ }
+ return fetch(`${URL}/movies/`, options).then(response => response.json());
+}
+
+// Delete
+export function destroyMovie(id) {
+ const options = { method: "DELETE" };
+ return fetch(`${URL}/movies/${id}`, options);
+}
+
+// Index/Get all
export function getAllMovies() {
- return;
+ return fetch(`${URL}/movies`).then((response) => response.json());
+}
+
+// Show/Get one
+export function getOneMovie(id) {
+ return fetch(`${URL}/movies/${id}`).then((response) => response.json());
+}
+
+// Update
+export function updateMovie(id, movie) {
+ const options = {
+ method: "PUT",
+ body: JSON.stringify(movie),
+ headers: { "Content-Type": "application/json" },
+ }
+ return fetch(`${URL}/movies/${id}`, options).then((response) => response.json());
}
diff --git a/src/components/common/Footer.js b/src/components/common/Footer.js
index 14d0023..ed1d77f 100644
--- a/src/components/common/Footer.js
+++ b/src/components/common/Footer.js
@@ -3,7 +3,7 @@ import "./Footer.css";
export default function Footer() {
return (
);
}
diff --git a/src/components/movies/Movie.css b/src/components/movies/Movie.css
new file mode 100644
index 0000000..a068691
--- /dev/null
+++ b/src/components/movies/Movie.css
@@ -0,0 +1,47 @@
+.movies-movie-wrapper {
+ margin: 0 auto;
+ width: 70%;
+ }
+
+ .movies-movie {
+ border: 1px solid var(--primary);
+ border-radius: 3px;
+ color: var(--gray);
+ display: grid;
+ grid-template-rows: auto 1fr auto;
+ }
+
+ .movies-movie article {
+ padding: 10px 25px;
+ }
+
+ .movies-movie aside {
+ background: var(--primary);
+ color: var(--light);
+ }
+
+ .movies-movie aside p {
+ display: inline-block;
+ font-size: 14px;
+ margin: 10px 25px;
+ padding: 0;
+ width: auto;
+ }
+
+ .movies-movie aside span {
+ font-weight: bold;
+ }
+
+ .movies-movie aside button {
+ background: none;
+ border: none;
+ color: var(--light);
+ cursor: pointer;
+ margin: 10px 25px;
+ text-decoration: underline;
+ }
+
+ .movies-movie aside button:hover {
+ text-decoration: none;
+ }
+
\ No newline at end of file
diff --git a/src/components/movies/Movie.js b/src/components/movies/Movie.js
new file mode 100644
index 0000000..a5ef306
--- /dev/null
+++ b/src/components/movies/Movie.js
@@ -0,0 +1,83 @@
+import { useState, useEffect } from "react";
+import { Link, useParams, useNavigate } from "react-router-dom";
+import { getOneMovie, destroyMovie } from "../../api/fetch";
+
+import "./Movie.css";
+
+import ErrorMessage from "../errors/ErrorMessage";
+
+function Movie() {
+ const [movie, setMovie] = useState({});
+ const [loadingError, setLoadingError] = useState(false);
+
+ const { id } = useParams();
+ let navigate = useNavigate();
+
+ useEffect(() => {
+ getOneMovie(id)
+ .then((response) => {
+ setMovie(response);
+ if (Object.keys(response) === 0) {
+ setLoadingError(true);
+ } else {
+ setLoadingError(false);
+ }
+ })
+ .catch((error) => {
+ setLoadingError(true);
+ });
+ }, [id]);
+
+ function handleDelete() {
+ destroyMovie(id)
+ .then(() => navigate("/movies"))
+ .catch((error) => {
+ console.log(error);
+ setLoadingError(true);
+ });
+ }
+
+ return (
+
+ {movie.title}
+
+ {loadingError ? (
+
+ ) : (
+ <>
+
+
+ {movie.description}
+
+
+ >
+ )}
+
+
+ );
+}
+
+export default Movie;
diff --git a/src/components/movies/MovieListing.css b/src/components/movies/MovieListing.css
new file mode 100644
index 0000000..332a4a4
--- /dev/null
+++ b/src/components/movies/MovieListing.css
@@ -0,0 +1,39 @@
+.movie {
+ border: 1px solid var(--primary);
+ border-radius: 3px;
+ color: var(--gray);
+ display: grid;
+ grid-template-rows: auto 1fr auto;
+ padding: 10px 25px;
+ }
+
+ .movie a {
+ border-bottom: 1px solid var(--primary);
+ color: var(--primary);
+ text-decoration: none;
+ }
+
+ .movie .title {
+ text-decoration: none;
+ font-variant: small-caps;
+ margin-bottom: 0;
+ }
+
+ .movie .description {
+ font-size: 14px;
+ }
+
+ .movie .details {
+ border-top: 1px solid var(--primary);
+ padding-top: 10px;
+ }
+
+ .movie .details p {
+ font-size: 12px;
+ margin: 0 0 5px;
+ }
+
+ .movie .details span {
+ font-weight: bold;
+ }
+
\ No newline at end of file
diff --git a/src/components/movies/MovieListing.js b/src/components/movies/MovieListing.js
new file mode 100644
index 0000000..e2c219d
--- /dev/null
+++ b/src/components/movies/MovieListing.js
@@ -0,0 +1,22 @@
+import { Link } from "react-router-dom";
+import "./MovieListing.css";
+
+export default function MovieListing({ movie }) {
+ return (
+
+
+ {movie.title}
+
+ {movie.description}
+
+
+ );
+}
diff --git a/src/components/movies/MoviesEditForm.js b/src/components/movies/MoviesEditForm.js
new file mode 100644
index 0000000..282c894
--- /dev/null
+++ b/src/components/movies/MoviesEditForm.js
@@ -0,0 +1,130 @@
+import { useState, useEffect } from "react";
+// import "./MoviesNewForm.css";
+import { updateMovie, getOneMovie } from "../../api/fetch";
+import { useNavigate, useParams } from "react-router-dom";
+
+export default function MoviesEditForm() {
+ const [movie, setMovie] = useState({
+ type: "",
+ title: "",
+ country: "",
+ dateAdded: "",
+ description: "",
+ duration: "",
+ listedIn: "",
+ rating: "",
+ releaseYear: "",
+ });
+
+ let navigate = useNavigate();
+ const { id } = useParams();
+
+ useEffect(() => {
+ getOneMovie(id)
+ .then((response) => {
+ setMovie(response);
+ })
+ .catch(error => {
+ console.log(error);
+ })
+ }, [id]);
+
+ function handleSubmit(event) {
+ event.preventDefault();
+
+ updateMovie(id, movie)
+ .then(() => {
+ navigate(`/movies/${id}`);
+ })
+ .catch(error => {
+ console.log(error);
+ });
+ }
+
+ function handleTextChange(event) {
+ setMovie({
+ ...movie,
+ [event.target.id]: event.target.value,
+ });
+ }
+
+ return (
+
+ );
+}
diff --git a/src/components/movies/MoviesIndex.css b/src/components/movies/MoviesIndex.css
new file mode 100644
index 0000000..9e91b1c
--- /dev/null
+++ b/src/components/movies/MoviesIndex.css
@@ -0,0 +1,42 @@
+.movies-index-wrapper {
+ margin: 0 auto;
+ width: 70%;
+ }
+
+ .movies-index-wrapper label {
+ display: block;
+ width: 50%;
+ margin: auto;
+ text-align: center;
+ }
+
+ .movies-index {
+ display: grid;
+ gap: 15px;
+ grid-template-columns: repeat(2, 1fr);
+ margin: 20px 0;
+ }
+
+ button a {
+ color: var(--primary);
+ border-bottom: 2px solid var(--light);
+ font-weight: 500;
+ padding: 1em;
+ text-decoration: none;
+ }
+
+ @media screen and (max-width: 1000px) {
+ .movies-index-wrapper {
+ width: 90%;
+ }
+ .movies-index {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ }
+
+ @media screen and (max-width: 800px) {
+ .movies-index {
+ grid-template-columns: 1fr;
+ }
+ }
+
\ No newline at end of file
diff --git a/src/components/movies/MoviesIndex.js b/src/components/movies/MoviesIndex.js
index 28e9dd7..0a44144 100644
--- a/src/components/movies/MoviesIndex.js
+++ b/src/components/movies/MoviesIndex.js
@@ -1,3 +1,71 @@
+import { Link } from "react-router-dom";
+import { useState, useEffect } from "react";
+import { getAllMovies } from "../../api/fetch";
+import MovieListing from "./MovieListing";
+
+import ErrorMessage from "../errors/ErrorMessage";
+
+import "./MoviesIndex.css";
+
export default function MoviesIndex() {
- return Movie List
;
+ const [loadingError, setLoadingError] = useState(false);
+ const [movies, setMovies] = useState([]);
+ const [allMovies, setAllMovies] = useState([]);
+ const [searchTitle, setSearchTitle] = useState("");
+
+ useEffect(() => {
+ getAllMovies()
+ .then((response) => {
+ setAllMovies(response);
+ setMovies(response);
+ setLoadingError(false);
+ })
+ .catch((error) => {
+ console.log(error);
+ setLoadingError(true);
+ });
+ }, []);
+
+ function handleTextChange(event) {
+ const title = event.target.value;
+ const result = title.length ? filterMovies(title, allMovies) : allMovies;
+ setSearchTitle(title);
+ setMovies(result);
+ }
+
+ function filterMovies(search, movies) {
+ return movies.filter((movie) => {
+ return movie.title.toLowerCase().match(search.toLowerCase());
+ });
+ }
+
+ return (
+
+ {loadingError ? (
+
+ ) : (
+
+ )}
+
+ );
}
diff --git a/src/components/movies/MoviesNewForm.js b/src/components/movies/MoviesNewForm.js
new file mode 100644
index 0000000..8c4c86f
--- /dev/null
+++ b/src/components/movies/MoviesNewForm.js
@@ -0,0 +1,119 @@
+import { useState } from "react";
+import { createMovie } from "../../api/fetch";
+import { useNavigate } from "react-router-dom";
+
+// import "./MoviesForm.css";
+
+export default function MoviesNewForm() {
+ const [movie, setMovie] = useState({
+ type: "",
+ title: "",
+ country: "",
+ dateAdded: "",
+ description: "",
+ duration: "",
+ listedIn: "",
+ rating: "",
+ releaseYear: "",
+ });
+
+ let navigate = useNavigate();
+
+ function handleSubmit(event) {
+ event.preventDefault();
+ createMovie(movie)
+ .then(response => {
+ navigate(`/movies/${response.id}`);
+ })
+ .catch(error => {
+ console.log(error);
+ });
+ };
+
+ function handleTextChange(event) {
+ setMovie({
+ ...movie,
+ [event.target.id]: event.target.value,
+ });
+ }
+
+ return (
+
+ );
+}
diff --git a/src/components/shows/Show.js b/src/components/shows/Show.js
index 263bddf..693ab99 100644
--- a/src/components/shows/Show.js
+++ b/src/components/shows/Show.js
@@ -1,5 +1,6 @@
-import { useState } from "react";
-import { Link, useParams } from "react-router-dom";
+import { useState, useEffect } from "react";
+import { Link, useParams, useNavigate } from "react-router-dom";
+import { getOneShow, destroyShow } from "../../api/fetch";
import "./Show.css";
@@ -10,8 +11,31 @@ function Show() {
const [loadingError, setLoadingError] = useState(false);
const { id } = useParams();
+ let navigate = useNavigate();
- function handleDelete() {}
+ useEffect(() => {
+ getOneShow(id)
+ .then((response) => {
+ setShow(response);
+ if (Object.keys(response) === 0) {
+ setLoadingError(true);
+ } else {
+ setLoadingError(false);
+ }
+ })
+ .catch((error) => {
+ setLoadingError(true);
+ });
+ }, [id]);
+
+ function handleDelete() {
+ destroyShow(id)
+ .then(() => navigate("/shows"))
+ .catch((error) => {
+ console.log(error);
+ setLoadingError(true);
+ });
+ }
return (
diff --git a/src/components/shows/ShowsEditForm.js b/src/components/shows/ShowsEditForm.js
index b58f9bf..1dff90f 100644
--- a/src/components/shows/ShowsEditForm.js
+++ b/src/components/shows/ShowsEditForm.js
@@ -1,5 +1,7 @@
-import { useState } from "react";
+import { useState, useEffect } from "react";
import "./ShowsForm.css";
+import { updateShow, getOneShow } from "../../api/fetch";
+import { useNavigate, useParams } from "react-router-dom";
export default function ShowsForm() {
const [show, setShow] = useState({
@@ -14,7 +16,30 @@ export default function ShowsForm() {
releaseYear: "",
});
- function handleSubmit(event) {}
+ let navigate = useNavigate();
+ const { id } = useParams();
+
+ useEffect(() => {
+ getOneShow(id)
+ .then((response) => {
+ setShow(response);
+ })
+ .catch(error => {
+ console.log(error);
+ })
+ }, [id]);
+
+ function handleSubmit(event) {
+ event.preventDefault();
+
+ updateShow(id, show)
+ .then(() => {
+ navigate(`/shows/${id}`);
+ })
+ .catch(error => {
+ console.log(error);
+ });
+ }
function handleTextChange(event) {
setShow({
diff --git a/src/components/shows/ShowsIndex.js b/src/components/shows/ShowsIndex.js
index a25373b..bcf95bc 100644
--- a/src/components/shows/ShowsIndex.js
+++ b/src/components/shows/ShowsIndex.js
@@ -1,13 +1,47 @@
import { Link } from "react-router-dom";
+import { useState, useEffect } from "react";
+import { getAllShows } from "../../api/fetch";
+import ShowListing from "./ShowListing";
import ErrorMessage from "../errors/ErrorMessage";
import "./ShowsIndex.css";
export default function ShowsIndex() {
+ const [loadingError, setLoadingError] = useState(false);
+ const [shows, setShows] = useState([]);
+ const [allShows, setAllShows] = useState([]);
+ const [searchTitle, setSearchTitle] = useState("");
+
+ useEffect(() => {
+ getAllShows()
+ .then((response) => {
+ setAllShows(response);
+ setShows(response);
+ setLoadingError(false);
+ })
+ .catch((error) => {
+ console.log(error);
+ setLoadingError(true);
+ });
+ }, []);
+
+ function handleTextChange(event) {
+ const title = event.target.value;
+ const result = title.length ? filterShows(title, allShows) : allShows;
+ setSearchTitle(title);
+ setShows(result);
+ }
+
+ function filterShows(search, shows) {
+ return shows.filter((show) => {
+ return show.title.toLowerCase().match(search.toLowerCase());
+ });
+ }
+
return (
- {false ? (
+ {loadingError ? (
) : (
)}
diff --git a/src/components/shows/ShowsNewForm.js b/src/components/shows/ShowsNewForm.js
index ddcec1b..3d4f0a9 100644
--- a/src/components/shows/ShowsNewForm.js
+++ b/src/components/shows/ShowsNewForm.js
@@ -1,4 +1,6 @@
import { useState } from "react";
+import { createShow } from "../../api/fetch";
+import { useNavigate } from "react-router-dom";
import "./ShowsForm.css";
@@ -15,7 +17,18 @@ export default function ShowsForm() {
releaseYear: "",
});
- function handleSubmit(event) {}
+ let navigate = useNavigate();
+
+ function handleSubmit(event) {
+ event.preventDefault();
+ createShow(show)
+ .then(response => {
+ navigate(`/shows/${response.id}`)
+ })
+ .catch(error => {
+ console.log(error)
+ })
+ }
function handleTextChange(event) {
setShow({