Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.env
78 changes: 73 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,76 @@
# React + Vite
## Unit Assignment: Flixster

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Submitted by: Gabriella

Currently, two official plugins are available:
Estimated time spent: 25 hours spent in total

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
Deployed Application (optional): [Flixster Deployed Site](https://flixster-starter-jpsb.onrender.com)

### Application Features

#### CORE FEATURES


- [X] **Display Movies**
- [X] Users can view a list of current movies from The Movie Database API.
- [X] For each movie displayed, users can see its title, poster image, and votes.
- [X] Users can load more current movies by clicking a button at the bottom of the list (page should not be reloaded).
- [X] **Search Functionality**
- [X] Users can search for movies and view the results in a grid.
- [X] Users can clear results and view previous current movies displayed.
- [X] **Accessibility Features**
- [X] Website implements accessibility features (semantic HTML, color contrast, font sizing, alt text for images).
- [X] **Responsive Design**
- [X] Website implements responsive web design.
- [X] **Movie Details**
- [X] Users can view more details about a movie in a popup, such as runtime in minutes, backdrop poster, release date, genres, and/or an overview.
- [X] **Sorting Options**
- [X] Users can click on a filter by drop down to sort product by type (alphabetic, release date, rating).
- [X] **Layout**
- [X] Website displays header, banner, search, movie grid, about, contact, and footer section.

#### STRETCH FEATURES

- [X] **Deployment**
- [X] Website is deployed via Render.
- [X] **Embedded Movie Trailers**
- [X] Within the popup displaying a movie's details, users can play the movie trailer.
- [X] **Watched Checkbox**
- [X] For each movie displayed, users can mark the movie as watched.
- [X] **Favorite Button**
- [X] For each movie displayed, users can favorite the movie.
- [X] **Sidebar**
- [X] Users can open a sidebar
- [X] The sidebar displays the user's favorited and watched movies

### Walkthrough Video
<div>
<a href="https://www.loom.com/share/9d2833a0b43540df8a0b41f195c7d256">
<p>Exploring New Features on My Website - Watch Video</p>
</a>
<a href="https://www.loom.com/share/9d2833a0b43540df8a0b41f195c7d256">
<img style="max-width:300px;" src="https://cdn.loom.com/sessions/thumbnails/9d2833a0b43540df8a0b41f195c7d256-with-play.gif">
</a>
</div>

### Reflection

* Did the topics discussed in your labs prepare you to complete the assignment? Be specific, which features in your weekly assignment did you feel unprepared to complete?

Yes the labs did prepare me

* If you had more time, what would you have done differently? Would you have added additional features? Changed the way your project responded to a particular event, etc.

I would have added users be able to click on the movies in the favorite tab and the modal for the movie dsplays.

* Reflect on your project demo, what went well? Were there things that maybe didn't go as planned? Did you notice something that your peer did that you would like to try next time?

I think being able to do most of the stretch feautres was the best thing. One thing that did not go as planned was having to move code around because i didnt understand passing props properling and how states should be higher up in componenets that need it.

### Open-source libraries used

- Add any links to open-source libraries used in your project.

### Shout out

Shout out to Theo
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/movie.png" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Flixster</title>
</head>
Expand Down
5 changes: 3 additions & 2 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
width: 100%;
}

.search-bar {
.searchBar {
flex-direction: column;
gap: 10px;
}

.search-bar form {
.searchBar form {
flex-direction: column;
}
}

68 changes: 63 additions & 5 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,68 @@
import { useState } from 'react'
import { useState, useEffect } from 'react'
import './App.css'
import Header from "./components/Header";
import.meta.env.VITE_API_KEY;
import NowPlayingScreen from './components/NowPlayingScreen';
import SearchScreen from './components/SearchScreen.jsx';
import Footer from './components/Footer.jsx';

const App = () => {
<div className="App">

</div>
const [isSearching, setSearching] = useState(false);
const [criteria, setCriteria] = useState("");

const [isFave, setFave] = useState([]);
const [isWatched, setWatched] = useState([]);

function loadFromLocalStorage() {
let savedLists = localStorage.getItem("userLists");
if (savedLists == undefined || savedLists == null ||savedLists == "") {
return;
}
let ListsObject = JSON.parse(savedLists);
setFave(ListsObject.favList);
setWatched(ListsObject.watchedList);
}

/**
* If the watchlist are now empty, check for stored (usually after a page load)
* else: put the current state into localstorage
*/
useEffect(() => {
if (isWatched.length == 0 && isFave.length == 0) {
loadFromLocalStorage()
} else {
localStorage.setItem("userLists", JSON.stringify(
{
favList: isFave,
watchedList: isWatched
}
))
}
}, [isFave, isWatched])

const handleNewCriteria = (criteriaFromHeader) => {
setCriteria(criteriaFromHeader)
}

const handleSetFav = (newVal) => {
setFave(newVal);
}

const handleSetWatched = (newVal) => {
setWatched(newVal);
}

return (
<div className="App">
<Header sortMovies={handleNewCriteria} setSearching={setSearching} />
{!isSearching ?
<NowPlayingScreen criteriaFromHeader={criteria} isFave={isFave} setFave={handleSetFav} isWatched={isWatched} setWatched={handleSetWatched}/>
:
<SearchScreen isFave={isFave} setFave={handleSetFav} isWatched={isWatched} setWatched={handleSetWatched}/>
}
<Footer />
</div>
);
}

export default App
export default App;
10 changes: 10 additions & 0 deletions src/components/FavMovies.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function FavMovies(props){
return(
<div>
<img src= {"https://image.tmdb.org/t/p/w500"+props.image} width="100" height="100"></img>
<p>{props.title}</p>
</div>
)
}

export default FavMovies
59 changes: 59 additions & 0 deletions src/components/Footer.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.footer {
background-color: #141414;
color: white;
padding: 20px 0;
text-align: center;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

.footer-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}

.footer-links {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 20px;
}

.footer-link {
color: #e50914;
margin: 0 10px;
text-decoration: none;
transition: color 0.3s ease;
}

.footer-link:hover {
color: #ff1e1e;
}

.footer-social {
margin-bottom: 20px;
}

.social-link {
color: white;
margin: 0 10px;
text-decoration: none;
font-size: 18px;
transition: color 0.3s ease;
}

.social-link:hover {
color: #e50914;
}

.footer-bottom {
margin-top: 20px;
}

.footer-bottom p {
margin: 0;
font-size: 14px;
color: #888;
}
29 changes: 29 additions & 0 deletions src/components/Footer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import './Footer.css';

const Footer = () => {
return (
<footer className="footer">
<div className="footer-content">
<div className="footer-links">
<a href="#" className="footer-link">FAQ</a>
<a href="#" className="footer-link">Help Center</a>
<a href="#" className="footer-link">Terms of Use</a>
<a href="#" className="footer-link">Privacy</a>
<a href="#" className="footer-link">Cookie Preferences</a>
<a href="#" className="footer-link">Corporate Information</a>
</div>
<div className="footer-social">
<a href="#" className="social-link">Facebook</a>
<a href="#" className="social-link">Instagram</a>
<a href="#" className="social-link">Twitter</a>
<a href="#" className="social-link">YouTube</a>
</div>
</div>
<div className="footer-bottom">
<p>&copy; 2024 Gabriella, Inc.</p>
</div>
</footer>
);
};

export default Footer;
44 changes: 44 additions & 0 deletions src/components/Header.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.header {
background-color:red;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
}

.header h1 {
margin: 0;
}

.toggleButton {
display: flex;
align-items: center;
}

.header input {
padding: 5px;
font-size: 16px;
border: none;
border-radius: 5px;
margin-right: 10px;
}

.header button {
background-color: white;
color: #ff0000;
border: none;
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
margin-right: 10px;
}

.header .sortBy {
background-color: #ff0000;
color: white;
border: none;
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
}
21 changes: 21 additions & 0 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import './Header.css';

function Header({setSearching,sortMovies }) {

return (
<div className="header">
<h1>Flixster</h1>
<div className="toggleButton">
<button onClick={() => setSearching(false)}>Now Playing</button>
<button onClick={() => setSearching(true)}>Search</button>
<select className="sortBy" onChange={(e) => sortMovies(e.target.value)}>
<option value="title">Sort by Title</option>
<option value="date">Sort by Date</option>
<option value="rating">Sort by Rating</option>
</select>
</div>
</div>
);
}

export default Header;
Loading