From ffdd48bf9fe58c7104c1636a834358402cf45f7f Mon Sep 17 00:00:00 2001 From: japsare ayushi Date: Wed, 9 Oct 2024 17:37:26 +0530 Subject: [PATCH 1/3] Migration to tailwind CSS --- package-lock.json | 43 ++++++- package.json | 7 +- postcss.config.js | 6 + src/Pages/Home.css | 142 +++++++++++++--------- src/Pages/Home.js | 98 ++++++++------- src/Pages/MovieDetails.js | 184 +++++++++++++---------------- src/components/Navbar.js | 163 +++++++++++++------------ src/components/Search.css | 1 - src/components/useThemeSwitcher.js | 18 +++ src/index.css | 3 + tailwind.config.js | 12 +- 11 files changed, 389 insertions(+), 288 deletions(-) create mode 100644 postcss.config.js create mode 100644 src/components/useThemeSwitcher.js diff --git a/package-lock.json b/package-lock.json index fe7cdff..df083f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,11 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "firebase": "^10.14.0", + "qrcode.react": "^4.0.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.3.0", + "react-qr-code": "^2.0.15", "react-router-dom": "^6.23.1", "react-scripts": "^5.0.1", "web-vitals": "^2.1.4" @@ -24,7 +26,9 @@ "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "babel-preset-react-app": "^10.0.1", - "tailwindcss": "^3.4.13" + "tailwindcss": "^3.4.13", + "html5-qrcode": "^2.3.8" + } }, "node_modules/@adobe/css-tools": { @@ -10159,6 +10163,13 @@ } } }, + "node_modules/html5-qrcode": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/html5-qrcode/-/html5-qrcode-2.3.8.tgz", + "integrity": "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/htmlparser2": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", @@ -15586,6 +15597,21 @@ "teleport": ">=0.2.0" } }, + "node_modules/qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==", + "license": "MIT" + }, + "node_modules/qrcode.react": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.0.1.tgz", + "integrity": "sha512-Lpj0tPBn561WiQ3QQWXbkx8xTtB8BZkJeMZWLJIL8iaPBCoWzW1IpCeU3gY5MDqsb0+efCvEGkl9O0naP64crA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -15918,6 +15944,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-qr-code": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.15.tgz", + "integrity": "sha512-MkZcjEXqVKqXEIMVE0mbcGgDpkfSdd8zhuzXEl9QzYeNcw8Hq2oVIzDLWuZN2PQBwM5PWjc2S31K8Q1UbcFMfw==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1", + "qr.js": "0.0.0" + }, + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -19243,4 +19282,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 42f3899..5c18812 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,11 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "firebase": "^10.14.0", + "qrcode.react": "^4.0.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.3.0", + "react-qr-code": "^2.0.15", "react-router-dom": "^6.23.1", "react-scripts": "^5.0.1", "web-vitals": "^2.1.4" @@ -43,6 +45,7 @@ "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "babel-preset-react-app": "^10.0.1", - "tailwindcss": "^3.4.13" + "tailwindcss": "^3.4.13", + "html5-qrcode": "^2.3.8" } -} +} \ No newline at end of file diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/src/Pages/Home.css b/src/Pages/Home.css index ae9a264..0b4a1ca 100644 --- a/src/Pages/Home.css +++ b/src/Pages/Home.css @@ -1,84 +1,110 @@ body{ - background-color: white; + background-color: white; } .poster { - width: 222px; - margin-right: 40px; - margin-bottom: 30px; + width: 222px; + margin-right: 40px; + margin-bottom: 30px; } img { - width: 222px; - height: 340px; - border-radius: 8px; + width: 222px; + height: 340px; + border-radius: 8px; } a{ - text-decoration: none; - color: black; + text-decoration: none; + color: black; } .myt-10 { - margin-top: 10px; + margin-top: 10px; } .myt-5 { - margin-top: 3px; + margin-top: 3px; } .poster-container { - display: flex; - justify-content: flex-start; - flex-wrap: wrap; - margin-top: 40px; - width: 90vw; - margin-left: 5vw; - margin-right: 5vw; + display: flex; + justify-content: flex-start; + flex-wrap: wrap; + margin-top: 40px; + width: 90vw; + margin-left: 5vw; + margin-right: 5vw; } .placeholder { - width: 222px; - height: 340px; - } - .faux-image-wrapper { - margin-bottom: 10px; - height: inherit; - /* position: relative; */ - padding-bottom: 100%; + width: 222px; + height: 340px; +} +.faux-image-wrapper { + margin-bottom: 10px; + height: inherit; + /* position: relative; */ + padding-bottom: 100%; +} +.faux-image { + background: #dddddd; + border-radius: 4px; + height: 100%; + /* position: absolute; */ + width: 100%; +} + +.shimmer { + overflow: hidden; + /* position: relative; */ +} + +.shimmer::before { + content: ""; + position: absolute; + background: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.4) 50%, + rgba(255, 255, 255, 0) 100% + ); + height: 100%; + width: 100%; + z-index: 1; + animation: shimmer 1s infinite; +} + +@keyframes shimmer { + 0% { + transform: translateX(-100%); } - .faux-image { - background: #dddddd; - border-radius: 4px; - height: 100%; - /* position: absolute; */ - width: 100%; + 100% { + transform: translateX(100%); } +} - .shimmer { - overflow: hidden; - /* position: relative; */ - } +.genre-buttons { + display: flex; + justify-content: center; + margin-bottom: 20px; +} - .shimmer::before { - content: ""; - position: absolute; - background: linear-gradient( - 90deg, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.4) 50%, - rgba(255, 255, 255, 0) 100% - ); - height: 100%; - width: 100%; - z-index: 1; - animation: shimmer 1s infinite; - } +.genre-button { + padding: 10px 20px; + margin: 0 10px; + border: none; + border-radius: 15px; + background-color: lightgray; + cursor: pointer; + transition: background-color 0.3s; + font-weight: 600; +} + +.genre-button:hover { + background-color: #ddd; +} - @keyframes shimmer { - 0% { - transform: translateX(-100%); - } - 100% { - transform: translateX(100%); - } - } \ No newline at end of file +.genre-button.active { + background-color: #ff4747; + color: white; +} \ No newline at end of file diff --git a/src/Pages/Home.js b/src/Pages/Home.js index bd6dc53..94f7ccf 100644 --- a/src/Pages/Home.js +++ b/src/Pages/Home.js @@ -1,8 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useFirebase } from "../context/firebase"; -import './Home.css'; import { Link, useNavigate } from 'react-router-dom'; -import { getAuth, onAuthStateChanged, signOut } from "firebase/auth"; +import { getAuth, onAuthStateChanged } from "firebase/auth"; import Footer from '../components/Footer'; import Search from '../components/Search'; @@ -10,30 +9,39 @@ const Home = () => { const firebase = useFirebase(); const [allMovies, setAllMovies] = useState([]); const [posterPaths, setPosterPaths] = useState({}); - const [isImageLoaded, setIsImageLoaded] = useState(false); - + const [isDarkMode, setIsDarkMode] = useState(false); const navigate = useNavigate(); const auth = getAuth(); + // Redirect to login if user is not authenticated onAuthStateChanged(auth, (user) => { if (!user) { - navigate("/Login") + navigate("/Login"); } }); + // Check for theme preference in local storage + useEffect(() => { + const storedTheme = localStorage.getItem('theme'); + if (storedTheme === 'dark') { + setIsDarkMode(true); + } + }, []); + const handleImageLoad = () => { setIsImageLoaded(true); - } + }; + // Fetch movies and their poster paths useEffect(() => { const fetchMovies = async () => { try { const movies = await firebase.fetchAllMovies(); setAllMovies(movies); + // Fetch poster paths for all movies const paths = await Promise.all(movies.map(movie => firebase.fetchMoviePoster(movie.movieId))); - // Create an object with movie IDs as keys and poster paths as values const posterPathsObj = {}; movies.forEach((movie, index) => { posterPathsObj[movie.movieId] = paths[index]; @@ -46,54 +54,64 @@ const Home = () => { document.title = 'Home - Book My Show'; fetchMovies(); - }, []); + }, [firebase]); return ( - <> - -
+
+
+ +
+ +
{allMovies.map((movie, index) => { const { movieReleaseDate, movieTitle, movieGenre, movieId } = movie; const posterPath = posterPaths[movie.movieId]; return ( - -
-
- -
-
- + +
+
+ {!isImageLoaded && ( +
+ +
+ )} +
+
+
-
{movieReleaseDate}
-
{movieTitle}
-
{movieGenre}
+
{movieReleaseDate}
+
{movieTitle}
+
{movieGenre}
); })}
+
- +
); }; diff --git a/src/Pages/MovieDetails.js b/src/Pages/MovieDetails.js index aa25238..57a40c4 100644 --- a/src/Pages/MovieDetails.js +++ b/src/Pages/MovieDetails.js @@ -3,14 +3,12 @@ import { getAuth, onAuthStateChanged } from "firebase/auth"; import { useNavigate } from "react-router-dom"; import { useFirebase } from "../context/firebase"; import { useParams, Link } from "react-router-dom"; -import "./MovieDetails.css"; import star from "../assets/icons/star.png"; import closeIcon from "../assets/icons/close-icon.png"; import Footer from '../components/Footer'; const MovieDetails = () => { const movieId = useParams(); - const firebase = useFirebase(); const auth = getAuth(); const navigate = useNavigate(); @@ -44,8 +42,7 @@ const MovieDetails = () => { totalRating += value; }); setAverageRating((totalRating / Object.keys(x.rating).length).toFixed(2) + '/5.0 (' + Object.keys(x.rating).length + ' Votes)'); - } - else { + } else { setAverageRating("0.0/5.0 (0 Votes)"); } } catch (error) { @@ -71,119 +68,96 @@ const MovieDetails = () => { return ( <> -
-
-
- -
-

- {movieDetails.movieTitle} -

- -
-
- -

{averageRating}

-
- -
-
- {movieDetails.movieLanguage} -
-
-
- {movieDetails.movieDuration} -
- -
- {movieDetails.movieGenre} -
- -
- {movieDetails.movieReleaseDate} -
-
- - Book Tickets +
+ + {/* Gradient Layer */} +
+
+ + + + {/* Content Layer */} +
+ Movie Poster +
+

{movieDetails.movieTitle}

+ +
+
+ Star Icon +

{averageRating}

+ +
+ +
+ {movieDetails.movieLanguage} +
+ +
+ {movieDetails.movieDuration} + + {movieDetails.movieGenre} + + {movieDetails.movieReleaseDate}
+ + + Book Tickets +
+
+ + + + -
-
-

About the movie

+
+
+

About the movie

{movieDetails.aboutMovie}

-

-
-

-

Cast

+
+
+
+

Cast

{movieDetails.movieCast}

-
-
- setShowRatingPopup(false)} /> - Rate Movie -
- setRating(5)} - /> - - setRating(4)} - /> - - setRating(3)} - /> - - setRating(2)} - /> - - setRating(1)} - /> - -
- +
+
+ setShowRatingPopup(false)} alt="Close" /> + Rate Movie +
+
+ {[5, 4, 3, 2, 1].map((starValue) => ( +
+ setRating(starValue)} + className="hidden" + /> + +
+ ))}
+
+
); diff --git a/src/components/Navbar.js b/src/components/Navbar.js index 4cf8ecf..7cc581b 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -1,31 +1,35 @@ import React, { useEffect, useRef, useState } from 'react'; import { Link, NavLink } from 'react-router-dom'; import { useFirebase } from '../context/firebase'; +import useThemeSwitcher from './useThemeSwitcher'; import { getAuth, onAuthStateChanged, signOut } from 'firebase/auth'; +import { FiMoon, FiSun } from 'react-icons/fi'; import './Navbar.css'; export default function Navbar() { const firebase = useFirebase(); const auth = getAuth(); - const detailsRef = useRef(null); // Create a reference to the
element + const detailsRef = useRef(null); + + + const [activeTheme, setTheme] = useThemeSwitcher(); // Function to close the dropdown when an item is clicked const handleDropdownClose = () => { if (detailsRef.current) { - detailsRef.current.removeAttribute('open'); // Close the
element + detailsRef.current.removeAttribute('open'); } }; + const handleClickOutside = (event) => { if (detailsRef.current && !detailsRef.current.contains(event.target)) { handleDropdownClose(); } }; - + useEffect(() => { - // Add event listener to detect clicks outside the dropdown document.addEventListener('click', handleClickOutside); return () => { - // Cleanup event listener when component unmounts document.removeEventListener('click', handleClickOutside); }; }, []); @@ -35,6 +39,7 @@ export default function Navbar() { const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [userId, setUserId] = useState(null); + useEffect(() => { const getUserData = onAuthStateChanged(auth, async (user) => { if (user) { @@ -64,84 +69,81 @@ export default function Navbar() { }, [auth]); const handleLogout = () => { - signOut(auth) - } + signOut(auth); + }; + + return ( <> - + {/* */} ) -} \ No newline at end of file +} diff --git a/src/components/Search.css b/src/components/Search.css index 73c0667..f6927c4 100644 --- a/src/components/Search.css +++ b/src/components/Search.css @@ -1,5 +1,4 @@ .search-wrapper{ - margin: 2rem auto; position: relative; width: 70vw; } diff --git a/src/components/useThemeSwitcher.js b/src/components/useThemeSwitcher.js new file mode 100644 index 0000000..4fdddf1 --- /dev/null +++ b/src/components/useThemeSwitcher.js @@ -0,0 +1,18 @@ +import { useEffect, useState } from 'react'; + +const useThemeSwitcher = () => { + const [theme, setTheme] = useState(localStorage.theme) || 'light'; + const activeTheme = theme === 'dark' ? 'light' : 'dark'; + + useEffect(() => { + const root = window.document.documentElement; + + root.classList.remove(activeTheme); + root.classList.add(theme); + localStorage.setItem('theme', theme); + }, [theme, activeTheme]); + + return [activeTheme, setTheme]; +}; + +export default useThemeSwitcher; \ No newline at end of file diff --git a/src/index.css b/src/index.css index a10392f..b40664d 100644 --- a/src/index.css +++ b/src/index.css @@ -1,6 +1,9 @@ @tailwind base; @tailwind components; @tailwind utilities; +*{ + @apply transition-colors duration-300 +} body { margin: 0; diff --git a/tailwind.config.js b/tailwind.config.js index dc4d768..c6bac4b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,9 +1,11 @@ -/** @type {import('tailwindcss').Config} */ module.exports = { - content: ["./src/**/*.{js,jsx,ts,tsx}",], + content: [ + "./src/**/*.{js,jsx,ts,tsx}", // Adjust according to your file extensions + ], theme: { - extend: {}, + extend: { + }, }, plugins: [], -} - + darkMode: 'class', +}; From 75487f80c40dc57c18c138c0d0fe3717835d8acb Mon Sep 17 00:00:00 2001 From: japsare ayushi Date: Wed, 9 Oct 2024 18:07:49 +0530 Subject: [PATCH 2/3] Migration to tailwind CSS --- src/Pages/MovieDetails.js | 62 +++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/Pages/MovieDetails.js b/src/Pages/MovieDetails.js index 57a40c4..4530078 100644 --- a/src/Pages/MovieDetails.js +++ b/src/Pages/MovieDetails.js @@ -13,7 +13,8 @@ const MovieDetails = () => { const auth = getAuth(); const navigate = useNavigate(); - const [rating, setRating] = useState(3); + const [rating, setRating] = useState(0); + const [hoveredStar, setHoveredStar] = useState(0); const [averageRating, setAverageRating] = useState(0); const [showRatingPopup, setShowRatingPopup] = useState(false); @@ -133,31 +134,42 @@ const MovieDetails = () => {
-
- setShowRatingPopup(false)} alt="Close" /> - Rate Movie -
-
- {[5, 4, 3, 2, 1].map((starValue) => ( -
- setRating(starValue)} - className="hidden" - /> - -
- ))} -
- +
+ setShowRatingPopup(false)} alt="Close" /> + Rate Movie
- +
+ {[1, 2, 3, 4, 5].map((starValue) => ( +
setHoveredStar(starValue)} // Set hovered star on mouse enter + onMouseLeave={() => setHoveredStar(0)} // Reset hovered star on mouse leave + > + setRating(starValue)} // Set rating on change + className="hidden" + /> + +
+ ))} +
+ +