├── src ├── assets │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── 5.png ├── store │ ├── reducers │ │ ├── rootReducer.js │ │ ├── nowPlaying.js │ │ └── movieReducer.js │ ├── constans │ │ └── index.js │ ├── configureStore.js │ └── actions │ │ ├── nowPlaying.js │ │ └── movie.js ├── index.js ├── pages │ ├── Home.jsx │ └── MovieDetail.jsx ├── components │ ├── CategoryItem.jsx │ ├── Category.jsx │ ├── Popular.jsx │ ├── Header.jsx │ ├── NowShowing.jsx │ ├── MovieList.jsx │ ├── MovieCard.jsx │ └── Search.jsx ├── App.js └── style.css ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── manifest.json └── index.html ├── tailwind.config.js ├── .gitignore ├── tailwind.css ├── package.json └── README.md /src/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ysnbyzli/the-movies/HEAD/src/assets/1.png -------------------------------------------------------------------------------- /src/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ysnbyzli/the-movies/HEAD/src/assets/2.png -------------------------------------------------------------------------------- /src/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ysnbyzli/the-movies/HEAD/src/assets/3.png -------------------------------------------------------------------------------- /src/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ysnbyzli/the-movies/HEAD/src/assets/4.png -------------------------------------------------------------------------------- /src/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ysnbyzli/the-movies/HEAD/src/assets/5.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ysnbyzli/the-movies/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ysnbyzli/the-movies/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ysnbyzli/the-movies/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/store/reducers/rootReducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import nowPlaying from './nowPlaying' 3 | import movieReducer from "./movieReducer"; 4 | 5 | 6 | export default combineReducers({ 7 | nowPlaying, 8 | movie: movieReducer 9 | }) -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: "jit", 3 | purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'], 4 | darkMode: 'class', // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /src/store/constans/index.js: -------------------------------------------------------------------------------- 1 | // NOW PLAYING 2 | export const FETCH_MOVIES_NOW_PLAYING = "FETCH_MOVIES_NOW_PLAYING"; 3 | 4 | // MOVIE 5 | export const CHANGE_CURRENT_CATEGORY = "CHANGE_CURRENT_CATEGORY" 6 | export const CHANGE_CURRENT_CATEGORY_MOVIES = "CHANGE_CURRENT_CATEGORY_MOVIES" 7 | export const FETCH_MOVIES_BY_POPULAR = "FETCH_MOVIES_BY_POPULAR" -------------------------------------------------------------------------------- /src/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, createStore } from "redux"; 2 | import { composeWithDevTools } from 'redux-devtools-extension'; 3 | import thunk from "redux-thunk"; 4 | import rootReducer from "./reducers/rootReducer"; 5 | 6 | export function configureStore() { 7 | return createStore(rootReducer, composeWithDevTools( 8 | applyMiddleware(thunk))); 9 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .env 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './style.css' 4 | import App from './App'; 5 | import { Provider } from 'react-redux'; 6 | import { configureStore } from './store/configureStore'; 7 | 8 | 9 | const store = configureStore(); 10 | 11 | ReactDOM.render( 12 | 13 | 14 | 15 | 16 | , 17 | document.getElementById('root') 18 | ); 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Category from '../components/Category' 3 | import MovieList from '../components/MovieList' 4 | import NowShowing from '../components/NowShowing' 5 | import Popular from '../components/Popular' 6 | 7 | const Home = () => { 8 | return ( 9 |
10 | 11 | 12 | 13 | 14 |
15 | ) 16 | } 17 | 18 | export default Home 19 | -------------------------------------------------------------------------------- /src/store/actions/nowPlaying.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { FETCH_MOVIES_NOW_PLAYING } from "../constans"; 3 | 4 | export const fetchNowPlayingList = () => { 5 | return async dispatch => { 6 | await axios.get(`https://api.themoviedb.org/3/movie/upcoming?api_key=${process.env.REACT_APP_API_KEY}&language=en-US&page=1`).then(value => dispatch({ 7 | type: FETCH_MOVIES_NOW_PLAYING, 8 | payload: value.data.results 9 | })) 10 | } 11 | } 12 | 13 | /*https://api.themoviedb.org/3/movie/now_playing?api_key=${process.env.REACT_APP_API_KEY}&language=en-US&page=1 */ -------------------------------------------------------------------------------- /src/store/reducers/nowPlaying.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable default-case */ 2 | /* eslint-disable import/no-anonymous-default-export */ 3 | 4 | import { FETCH_MOVIES_NOW_PLAYING } from "../constans"; 5 | 6 | const initialState = { 7 | nowPlayingMovies: [] 8 | } 9 | 10 | 11 | export default (state = initialState, action) => { 12 | switch (action.type) { 13 | case FETCH_MOVIES_NOW_PLAYING: 14 | return { 15 | nowPlayingMovies: [ 16 | action.payload 17 | ] 18 | } 19 | default: 20 | return state; 21 | } 22 | } -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/components/CategoryItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | import { changeCurrentCategory } from '../store/actions/movie'; 4 | 5 | const CategoryItem = ({ category: { id, name } }) => { 6 | 7 | 8 | const currentCategory = useSelector(state => state.movie.currentCategory) 9 | 10 | const dispatch = useDispatch(); 11 | 12 | const handleChangeCategory = (id) => { 13 | dispatch(changeCurrentCategory(id)) 14 | } 15 | 16 | return ( 17 | handleChangeCategory(id)}>{name} 18 | ) 19 | } 20 | 21 | export default CategoryItem 22 | -------------------------------------------------------------------------------- /src/components/Category.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CategoryItem from './CategoryItem' 3 | 4 | const categories = [ 5 | { id: 28, name: "Aksiyon" }, 6 | { id: 12, name: "Macera" }, 7 | { id: 16, name: "Animasyon" }, 8 | { id: 35, name: "Komedi" }, 9 | { id: 18, name: "Drama" }, 10 | ] 11 | 12 | 13 | const Category = () => { 14 | 15 | 16 | return ( 17 | <> 18 |
19 |

Kategoriler

20 |
21 | { 22 | categories.map(category => ( 23 | 24 | )) 25 | } 26 |
27 |
28 | 29 | ) 30 | } 31 | 32 | export default Category 33 | -------------------------------------------------------------------------------- /src/store/reducers/movieReducer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable default-case */ 2 | /* eslint-disable import/no-anonymous-default-export */ 3 | 4 | import { CHANGE_CURRENT_CATEGORY, CHANGE_CURRENT_CATEGORY_MOVIES, FETCH_MOVIES_BY_POPULAR } from "../constans"; 5 | 6 | 7 | const initialState = { 8 | currentCategory: 28, 9 | currentCategoryMovies: [], 10 | popularMovies: [] 11 | } 12 | 13 | export default (state = initialState, action) => { 14 | switch (action.type) { 15 | case CHANGE_CURRENT_CATEGORY: 16 | return { 17 | ...state, 18 | currentCategory: action.payload 19 | } 20 | case CHANGE_CURRENT_CATEGORY_MOVIES: 21 | return { 22 | ...state, 23 | currentCategoryMovies: action.payload 24 | } 25 | case FETCH_MOVIES_BY_POPULAR: 26 | return { 27 | ...state, 28 | popularMovies: action.payload 29 | } 30 | default: 31 | return state; 32 | } 33 | } -------------------------------------------------------------------------------- /tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @import url("https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css"); 6 | @import url("https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css"); 7 | @import url('https://fonts.googleapis.com/css2?family=Oswald:wght@200;300;400;500;600;700&display=swap'); 8 | 9 | @layer base { 10 | html, body{ 11 | font-family: 'Oswald', sans-serif; 12 | overflow-x: hidden; 13 | height: 100%; 14 | width: 100%; 15 | overflow-x: hidden; 16 | } 17 | } 18 | 19 | .toggle-checkbox:checked { 20 | @apply: right-0 border-green-400; 21 | right: 0; 22 | border-color: #68D391; 23 | } 24 | .toggle-checkbox:checked + .toggle-label { 25 | @apply: bg-green-400; 26 | background-color: #68D391; 27 | } 28 | 29 | .category-active{ 30 | @apply rounded-2xl bg-red-200 text-red-400 dark:bg-red-400 dark:text-red-100 dark:hover:text-red-100 31 | } 32 | 33 | .slick-list{ 34 | height: 100%; 35 | } -------------------------------------------------------------------------------- /src/components/Popular.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useEffect } from 'react'; 3 | import { useDispatch, useSelector } from 'react-redux' 4 | import { fecthMoviesByPopular } from '../store/actions/movie'; 5 | import MovieCard from './MovieCard'; 6 | 7 | const Popular = () => { 8 | 9 | const movies = useSelector(state => state.movie.popularMovies); 10 | 11 | const dispatch = useDispatch(); 12 | 13 | useEffect(() => { 14 | dispatch(fecthMoviesByPopular()) 15 | }, [dispatch]) 16 | 17 | return ( 18 |
19 |

Popüler

20 | 27 |
28 | ) 29 | } 30 | 31 | export default Popular 32 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import Header from "./components/Header"; 3 | import Home from "./pages/Home"; 4 | import { 5 | BrowserRouter as Router, 6 | Switch, 7 | Route, 8 | } from "react-router-dom"; 9 | import MovieDetail from "./pages/MovieDetail"; 10 | 11 | function App() { 12 | 13 | const [isDarkMode, setIsDarkMode] = useState(false); 14 | 15 | const handleChangeDarkMode = () => { 16 | setIsDarkMode(!isDarkMode); 17 | 18 | if (!isDarkMode) { 19 | document.body.classList.add('dark') 20 | } else { 21 | document.body.classList.remove('dark') 22 | } 23 | } 24 | 25 | 26 | return ( 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | ); 44 | } 45 | 46 | export default App; 47 | -------------------------------------------------------------------------------- /src/store/actions/movie.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import { CHANGE_CURRENT_CATEGORY, CHANGE_CURRENT_CATEGORY_MOVIES, FETCH_MOVIES_BY_POPULAR } from "../constans" 3 | 4 | export const changeCurrentCategory = (category) => { 5 | return async dispatch => { 6 | dispatch({ 7 | type: CHANGE_CURRENT_CATEGORY, 8 | payload: category 9 | }) 10 | } 11 | } 12 | 13 | export const changeCurrentCategoryMovies = (category_id) => { 14 | return async dispatch => { 15 | await axios.get(`https://api.themoviedb.org/3/discover/movie?api_key=${process.env.REACT_APP_API_KEY}&with_genres=${category_id}`).then(value => dispatch({ 16 | type: CHANGE_CURRENT_CATEGORY_MOVIES, 17 | payload: value.data.results 18 | })) 19 | } 20 | } 21 | 22 | export const fecthMoviesByPopular = () => { 23 | return async dispatch => { 24 | await axios.get(`https://api.themoviedb.org/3/movie/popular?api_key=${process.env.REACT_APP_API_KEY}&language=tr-TR&page=1`).then(value => dispatch({ 25 | type: FETCH_MOVIES_BY_POPULAR, 26 | payload: value.data.results 27 | })) 28 | } 29 | } -------------------------------------------------------------------------------- /src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | import Search from './Search'; 3 | 4 | const Header = ({ handleChangeDarkMode }) => { 5 | 6 | return ( 7 |
8 |
9 | 10 | The Movies 11 | 12 |
13 | 14 |
15 | handleChangeDarkMode()} /> 16 | 17 |
18 |
19 |
20 |
21 | ) 22 | } 23 | 24 | export default Header 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "the-movies", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "axios": "^0.21.1", 10 | "hoek": "^6.1.3", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-icons": "^4.2.0", 14 | "react-player": "^2.9.0", 15 | "react-redux": "^7.2.4", 16 | "react-router-dom": "^5.2.1", 17 | "react-scripts": "^4.0.3", 18 | "react-slick": "^0.28.1", 19 | "redux": "^4.1.1", 20 | "redux-devtools-extension": "^2.13.9", 21 | "redux-thunk": "^2.3.0", 22 | "web-vitals": "^1.0.1" 23 | }, 24 | "scripts": { 25 | "start": "react-scripts start", 26 | "build": "react-scripts build", 27 | "test": "react-scripts test", 28 | "eject": "react-scripts eject", 29 | "watch": "npx tailwindcss -i tailwind.css -o src/style.css --watch" 30 | }, 31 | "eslintConfig": { 32 | "extends": [ 33 | "react-app", 34 | "react-app/jest" 35 | ] 36 | }, 37 | "browserslist": { 38 | "production": [ 39 | ">0.2%", 40 | "not dead", 41 | "not op_mini all" 42 | ], 43 | "development": [ 44 | "last 1 chrome version", 45 | "last 1 firefox version", 46 | "last 1 safari version" 47 | ] 48 | }, 49 | "devDependencies": { 50 | "autoprefixer": "^10.3.3", 51 | "postcss": "^8.3.6", 52 | "tailwindcss": "^2.2.8" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/components/NowShowing.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useEffect } from 'react'; 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import Slider from 'react-slick'; 5 | import MovieCard from './MovieCard'; 6 | import { fetchNowPlayingList } from '../store/actions/nowPlaying' 7 | 8 | 9 | const settings = { 10 | infinite: false, 11 | speed: 500, 12 | slidesToShow: 4, 13 | slidesToScroll: 4, 14 | initialSlide: 0, 15 | arrows: false, 16 | responsive: [ 17 | { 18 | breakpoint: 1024, 19 | settings: { 20 | slidesToShow: 3, 21 | slidesToScroll: 3, 22 | infinite: true, 23 | } 24 | }, 25 | { 26 | breakpoint: 600, 27 | settings: { 28 | slidesToShow: 2, 29 | slidesToScroll: 2, 30 | initialSlide: 2 31 | } 32 | }, 33 | { 34 | breakpoint: 480, 35 | settings: { 36 | slidesToShow: 1, 37 | slidesToScroll: 1 38 | } 39 | } 40 | ] 41 | } 42 | 43 | 44 | const NowShowing = () => { 45 | 46 | const movies = useSelector(state => state.nowPlaying.nowPlayingMovies) 47 | 48 | 49 | const dispatch = useDispatch(); 50 | 51 | useEffect(() => { 52 | dispatch(fetchNowPlayingList()) 53 | }, [dispatch]) 54 | 55 | return ( 56 |
57 |
58 |

Sinemalarda

59 |
60 | 61 | {movies[0]?.map(movie => (
))} 62 |
63 |
64 | ) 65 | } 66 | 67 | export default NowShowing 68 | -------------------------------------------------------------------------------- /src/components/MovieList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useEffect } from 'react' 3 | import { useDispatch, useSelector } from 'react-redux' 4 | import Slider from 'react-slick' 5 | import { changeCurrentCategoryMovies } from '../store/actions/movie' 6 | import MovieCard from './MovieCard' 7 | 8 | const settings = { 9 | infinite: false, 10 | speed: 500, 11 | slidesToShow: 6, 12 | slidesToScroll: 6, 13 | initialSlide: 0, 14 | arrows: false, 15 | responsive: [ 16 | { 17 | breakpoint: 1440, 18 | settings: { 19 | slidesToShow: 5, 20 | slidesToScroll: 5, 21 | infinite: true, 22 | } 23 | }, 24 | { 25 | breakpoint: 1024, 26 | settings: { 27 | slidesToShow: 3, 28 | slidesToScroll: 3, 29 | infinite: true, 30 | } 31 | }, 32 | { 33 | breakpoint: 600, 34 | settings: { 35 | slidesToShow: 2, 36 | slidesToScroll: 2, 37 | initialSlide: 2 38 | } 39 | }, 40 | { 41 | breakpoint: 480, 42 | settings: { 43 | slidesToShow: 1, 44 | slidesToScroll: 1 45 | } 46 | } 47 | ] 48 | } 49 | 50 | const MovieList = () => { 51 | 52 | const currentCategoryId = useSelector(state => state.movie.currentCategory) 53 | const currentCategoryMovies = useSelector(state => state.movie.currentCategoryMovies) 54 | 55 | const dispatch = useDispatch(); 56 | 57 | useEffect(() => { 58 | dispatch(changeCurrentCategoryMovies(currentCategoryId)); 59 | }, [currentCategoryId, dispatch]) 60 | 61 | 62 | 63 | return ( 64 |
65 | 66 | {currentCategoryMovies && currentCategoryMovies.map(movie => ( 67 | 68 | ))} 69 | 70 |
71 | ) 72 | } 73 | 74 | export default MovieList 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # THE MOVIES 3 | 4 | Bu projede [themovie.db](https://www.themoviedb.org/?language=tr) api kullanarak film 5 | bilgileri ile bir proje ortaya çıkardım. Proje kapsamında redux kullanımı pekiştirmek için 6 | bir çok fırsatta kullanmaya çalıştım. 7 | 8 | ## DEMO 9 | https://heuristic-sinoussi-e1e26e.netlify.app/ 10 | 11 | ## Kullanılan Teknolojiler 12 | [![React Badge]( https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB)](https://tr.reactjs.org/) 13 | [![React Router](https://img.shields.io/badge/React_Router-CA4245?style=for-the-badge&logo=react-router&logoColor=white)](https://reactrouter.com/) 14 | [![Redux](https://img.shields.io/badge/Redux-593D88?style=for-the-badge&logo=redux&logoColor=white)](https://redux.js.org/) 15 | [![Tailwindcss](https://img.shields.io/badge/Tailwind_CSS-38B2AC?style=for-the-badge&logo=tailwind-css&logoColor=white)](https://tailwindcss.com/) 16 | 17 | ## Ekran Görüntüleri 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ## Özellikler 27 | 28 | - [x] Açık/koyu mod geçişi 29 | - [x] Responsive tasarım 30 | - [x] Sinemalarda olan filmlerin listelenmesi 31 | - [x] Kategoriye göre filtreleme 32 | - [x] Popüler filmleri listeleme 33 | - [x] Filme ait detay sayfası 34 | - [x] Film arama 35 | 36 | 37 | 38 | ## Ortam Değişkenleri 39 | 40 | Bu projeyi çalıştırmak için aşağıdaki ortam değişkenlerini .env dosyanıza eklemeniz gerekecek 41 | 42 | | Parametre | Açıklama | 43 | | :-------- | :------------------------- | 44 | | `REACT_APP_API_KEY` | **Gerekli**. API anahtarınız. | 45 | 46 | 47 | ## Bilgisayarınızda Çalıştırın 48 | 49 | Projeyi klonlayın 50 | 51 | ```bash 52 | git clone https://link-to-project 53 | ``` 54 | 55 | Proje dizinine gidin 56 | 57 | ```bash 58 | cd my-project 59 | ``` 60 | 61 | Gerekli paketleri yükleyin 62 | 63 | ```bash 64 | npm install 65 | ``` 66 | 67 | Sunucuyu çalıştırın 68 | 69 | ```bash 70 | npm run start 71 | ``` 72 | 73 | ## Geri Bildirim 74 | 75 | Herhangi bir geri bildiriminiz varsa, lütfen yasinbeyazli29@gmail.com adresinden bana 76 | ulaşabilirsiniz. 77 | 78 | -------------------------------------------------------------------------------- /src/components/MovieCard.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import React from 'react' 3 | import { useState } from 'react' 4 | import { useEffect } from 'react' 5 | import { AiFillStar } from 'react-icons/ai' 6 | import { Link } from 'react-router-dom' 7 | const MovieCard = ({ movie: { id, original_title, vote_average, vote_count, poster_path }, small, collection }) => { 8 | 9 | const [detail, setDetails] = useState([]); 10 | 11 | useEffect(() => { 12 | fetchMovieDetail(); 13 | }, []) 14 | 15 | const fetchMovieDetail = async () => { 16 | await axios.get(`https://api.themoviedb.org/3/movie/${id}?api_key=${process.env.REACT_APP_API_KEY}&language=tr-TR 17 | `).then(value => setDetails(value.data)); 18 | } 19 | 20 | return ( 21 |
22 | 23 | 24 | movie 25 | 26 | 27 |
28 |
29 |

{original_title}

30 | { 31 | detail?.genres?.map(genre => genre.name + " ") 32 | } 33 | {vote_average} ({vote_count}) 34 |
35 | { 36 | !small ? ( 37 |
38 | {detail.runtime} min 39 | {detail.release_date} 40 |
41 | ) : null 42 | } 43 |
44 |
45 | ) 46 | } 47 | 48 | export default MovieCard 49 | -------------------------------------------------------------------------------- /src/components/Search.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import React, { useState } from 'react' 3 | import { useEffect } from 'react'; 4 | import { BiSearch } from 'react-icons/bi'; 5 | import { Link } from 'react-router-dom'; 6 | 7 | const Search = () => { 8 | 9 | const [search, setSearch] = useState(""); 10 | const [movies, setMovies] = useState([]) 11 | 12 | 13 | useEffect(() => { 14 | if (search && search.length >= 3) { 15 | axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${process.env.REACT_APP_API_KEY}&language=en-US&query=${search}&page=1&include_adult=false`) 16 | .then(value => setMovies(value.data.results)) 17 | } else { 18 | setMovies([]) 19 | } 20 | }, [search]) 21 | 22 | 23 | return ( 24 |
25 | setSearch(e.target.value)} /> 27 | 30 | 48 |
49 | ) 50 | } 51 | 52 | export default Search 53 | -------------------------------------------------------------------------------- /src/pages/MovieDetail.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import React from 'react' 3 | import { useState } from 'react'; 4 | import { useEffect } from 'react'; 5 | import ReactPlayer from 'react-player/youtube'; 6 | import { Link, useParams } from 'react-router-dom' 7 | import { GoMute, GoUnmute } from 'react-icons/go' 8 | import { BsPlayFill } from 'react-icons/bs' 9 | import { AiOutlineArrowUp, AiOutlineArrowLeft } from 'react-icons/ai' 10 | 11 | const MovieDetail = () => { 12 | 13 | const [videos, setVideos] = useState(null); 14 | const [details, setDetails] = useState([]); 15 | const [isMuted, setIsMuted] = useState(true); 16 | const [isWatching, setIsWatching] = useState(false); 17 | 18 | const { id } = useParams(); 19 | 20 | 21 | 22 | useEffect(() => { 23 | fetchMovieVideo() 24 | fetchMovieDetail() 25 | }, [id]) 26 | 27 | const fetchMovieVideo = async () => { 28 | axios.get(`https://api.themoviedb.org/3/movie/${id}/videos?api_key=${process.env.REACT_APP_API_KEY}&language=tr-TR 29 | `).then(value => setVideos(value.data.results[0]?.key)) 30 | } 31 | 32 | const fetchMovieDetail = async () => { 33 | await axios.get(`https://api.themoviedb.org/3/movie/${id}?api_key=${process.env.REACT_APP_API_KEY}&language=tr-TR 34 | `).then(value => setDetails(value.data)); 35 | } 36 | 37 | const handleChangeMute = () => { 38 | setIsMuted(!isMuted); 39 | } 40 | 41 | const handleChangeIsWatching = () => { 42 | setIsWatching(!isWatching); 43 | } 44 | 45 | return ( 46 |
47 | 55 | { 56 | isWatching ? ( 57 | handleChangeIsWatching()} 60 | > 61 | 62 | 63 | ) : ( 64 |
65 | { 66 | videos ? (
67 | movie 68 |
) : ( 69 | movie 70 | ) 71 | } 72 | 73 |
74 |
75 |
76 |

77 | {details.original_title} 78 |

79 |
    80 | {details.genres?.map(genre => ( 81 |
  • • {genre.name}
  • 82 | ))} 83 |
84 |
85 | 86 | {details.release_date} 87 | 88 |
89 | { 90 | details.overview && (

91 |

ÖZET

92 | {details.overview} 93 |

) 94 | } 95 | 96 | 97 | { 98 | videos && ( 99 |
    100 |
  • 101 | handleChangeIsWatching()}>{ 102 | 103 | } 104 |
  • 105 |
  • handleChangeMute()}> 106 | { 107 | isMuted ? () : () 108 | } 109 |
  • 110 |
111 | ) 112 | } 113 | 114 |
115 |
116 | ) 117 | } 118 | 119 | 120 | 121 |
122 | ) 123 | } 124 | 125 | export default MovieDetail 126 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | /*! tailwindcss v2.2.8 | MIT License | https://tailwindcss.com */ 2 | 3 | /*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ 4 | 5 | /* 6 | Document 7 | ======== 8 | */ 9 | 10 | /** 11 | Use a better box model (opinionated). 12 | */ 13 | 14 | *, 15 | ::before, 16 | ::after { 17 | box-sizing: border-box; 18 | } 19 | 20 | /** 21 | Use a more readable tab size (opinionated). 22 | */ 23 | 24 | html { 25 | -moz-tab-size: 4; 26 | tab-size: 4; 27 | } 28 | 29 | /** 30 | 1. Correct the line height in all browsers. 31 | 2. Prevent adjustments of font size after orientation changes in iOS. 32 | */ 33 | 34 | html { 35 | line-height: 1.15; 36 | /* 1 */ 37 | -webkit-text-size-adjust: 100%; 38 | /* 2 */ 39 | } 40 | 41 | /* 42 | Sections 43 | ======== 44 | */ 45 | 46 | /** 47 | Remove the margin in all browsers. 48 | */ 49 | 50 | body { 51 | margin: 0; 52 | } 53 | 54 | /** 55 | Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) 56 | */ 57 | 58 | body { 59 | font-family: 60 | system-ui, 61 | -apple-system, /* Firefox supports this but not yet `system-ui` */ 62 | 'Segoe UI', 63 | Roboto, 64 | Helvetica, 65 | Arial, 66 | sans-serif, 67 | 'Apple Color Emoji', 68 | 'Segoe UI Emoji'; 69 | } 70 | 71 | /* 72 | Grouping content 73 | ================ 74 | */ 75 | 76 | /** 77 | 1. Add the correct height in Firefox. 78 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 79 | */ 80 | 81 | hr { 82 | height: 0; 83 | /* 1 */ 84 | color: inherit; 85 | /* 2 */ 86 | } 87 | 88 | /* 89 | Text-level semantics 90 | ==================== 91 | */ 92 | 93 | /** 94 | Add the correct text decoration in Chrome, Edge, and Safari. 95 | */ 96 | 97 | abbr[title] { 98 | -webkit-text-decoration: underline dotted; 99 | text-decoration: underline dotted; 100 | } 101 | 102 | /** 103 | Add the correct font weight in Edge and Safari. 104 | */ 105 | 106 | b, 107 | strong { 108 | font-weight: bolder; 109 | } 110 | 111 | /** 112 | 1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) 113 | 2. Correct the odd 'em' font sizing in all browsers. 114 | */ 115 | 116 | code, 117 | kbd, 118 | samp, 119 | pre { 120 | font-family: 121 | ui-monospace, 122 | SFMono-Regular, 123 | Consolas, 124 | 'Liberation Mono', 125 | Menlo, 126 | monospace; 127 | /* 1 */ 128 | font-size: 1em; 129 | /* 2 */ 130 | } 131 | 132 | /** 133 | Add the correct font size in all browsers. 134 | */ 135 | 136 | small { 137 | font-size: 80%; 138 | } 139 | 140 | /** 141 | Prevent 'sub' and 'sup' elements from affecting the line height in all browsers. 142 | */ 143 | 144 | sub, 145 | sup { 146 | font-size: 75%; 147 | line-height: 0; 148 | position: relative; 149 | vertical-align: baseline; 150 | } 151 | 152 | sub { 153 | bottom: -0.25em; 154 | } 155 | 156 | sup { 157 | top: -0.5em; 158 | } 159 | 160 | /* 161 | Tabular data 162 | ============ 163 | */ 164 | 165 | /** 166 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 167 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 168 | */ 169 | 170 | table { 171 | text-indent: 0; 172 | /* 1 */ 173 | border-color: inherit; 174 | /* 2 */ 175 | } 176 | 177 | /* 178 | Forms 179 | ===== 180 | */ 181 | 182 | /** 183 | 1. Change the font styles in all browsers. 184 | 2. Remove the margin in Firefox and Safari. 185 | */ 186 | 187 | button, 188 | input, 189 | optgroup, 190 | select, 191 | textarea { 192 | font-family: inherit; 193 | /* 1 */ 194 | font-size: 100%; 195 | /* 1 */ 196 | line-height: 1.15; 197 | /* 1 */ 198 | margin: 0; 199 | /* 2 */ 200 | } 201 | 202 | /** 203 | Remove the inheritance of text transform in Edge and Firefox. 204 | 1. Remove the inheritance of text transform in Firefox. 205 | */ 206 | 207 | button, 208 | select { 209 | /* 1 */ 210 | text-transform: none; 211 | } 212 | 213 | /** 214 | Correct the inability to style clickable types in iOS and Safari. 215 | */ 216 | 217 | button, 218 | [type='button'], 219 | [type='reset'], 220 | [type='submit'] { 221 | -webkit-appearance: button; 222 | } 223 | 224 | /** 225 | Remove the inner border and padding in Firefox. 226 | */ 227 | 228 | ::-moz-focus-inner { 229 | border-style: none; 230 | padding: 0; 231 | } 232 | 233 | /** 234 | Restore the focus styles unset by the previous rule. 235 | */ 236 | 237 | :-moz-focusring { 238 | outline: 1px dotted ButtonText; 239 | } 240 | 241 | /** 242 | Remove the additional ':invalid' styles in Firefox. 243 | See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737 244 | */ 245 | 246 | :-moz-ui-invalid { 247 | box-shadow: none; 248 | } 249 | 250 | /** 251 | Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers. 252 | */ 253 | 254 | legend { 255 | padding: 0; 256 | } 257 | 258 | /** 259 | Add the correct vertical alignment in Chrome and Firefox. 260 | */ 261 | 262 | progress { 263 | vertical-align: baseline; 264 | } 265 | 266 | /** 267 | Correct the cursor style of increment and decrement buttons in Safari. 268 | */ 269 | 270 | ::-webkit-inner-spin-button, 271 | ::-webkit-outer-spin-button { 272 | height: auto; 273 | } 274 | 275 | /** 276 | 1. Correct the odd appearance in Chrome and Safari. 277 | 2. Correct the outline style in Safari. 278 | */ 279 | 280 | [type='search'] { 281 | -webkit-appearance: textfield; 282 | /* 1 */ 283 | outline-offset: -2px; 284 | /* 2 */ 285 | } 286 | 287 | /** 288 | Remove the inner padding in Chrome and Safari on macOS. 289 | */ 290 | 291 | ::-webkit-search-decoration { 292 | -webkit-appearance: none; 293 | } 294 | 295 | /** 296 | 1. Correct the inability to style clickable types in iOS and Safari. 297 | 2. Change font properties to 'inherit' in Safari. 298 | */ 299 | 300 | ::-webkit-file-upload-button { 301 | -webkit-appearance: button; 302 | /* 1 */ 303 | font: inherit; 304 | /* 2 */ 305 | } 306 | 307 | /* 308 | Interactive 309 | =========== 310 | */ 311 | 312 | /* 313 | Add the correct display in Chrome and Safari. 314 | */ 315 | 316 | summary { 317 | display: list-item; 318 | } 319 | 320 | /** 321 | * Manually forked from SUIT CSS Base: https://github.com/suitcss/base 322 | * A thin layer on top of normalize.css that provides a starting point more 323 | * suitable for web applications. 324 | */ 325 | 326 | /** 327 | * Removes the default spacing and border for appropriate elements. 328 | */ 329 | 330 | blockquote, 331 | dl, 332 | dd, 333 | h1, 334 | h2, 335 | h3, 336 | h4, 337 | h5, 338 | h6, 339 | hr, 340 | figure, 341 | p, 342 | pre { 343 | margin: 0; 344 | } 345 | 346 | button { 347 | background-color: transparent; 348 | background-image: none; 349 | } 350 | 351 | fieldset { 352 | margin: 0; 353 | padding: 0; 354 | } 355 | 356 | ol, 357 | ul { 358 | list-style: none; 359 | margin: 0; 360 | padding: 0; 361 | } 362 | 363 | /** 364 | * Tailwind custom reset styles 365 | */ 366 | 367 | /** 368 | * 1. Use the user's configured `sans` font-family (with Tailwind's default 369 | * sans-serif font stack as a fallback) as a sane default. 370 | * 2. Use Tailwind's default "normal" line-height so the user isn't forced 371 | * to override it to ensure consistency even when using the default theme. 372 | */ 373 | 374 | html { 375 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 376 | /* 1 */ 377 | line-height: 1.5; 378 | /* 2 */ 379 | } 380 | 381 | /** 382 | * Inherit font-family and line-height from `html` so users can set them as 383 | * a class directly on the `html` element. 384 | */ 385 | 386 | body { 387 | font-family: inherit; 388 | line-height: inherit; 389 | } 390 | 391 | /** 392 | * 1. Prevent padding and border from affecting element width. 393 | * 394 | * We used to set this in the html element and inherit from 395 | * the parent element for everything else. This caused issues 396 | * in shadow-dom-enhanced elements like
where the content 397 | * is wrapped by a div with box-sizing set to `content-box`. 398 | * 399 | * https://github.com/mozdevs/cssremedy/issues/4 400 | * 401 | * 402 | * 2. Allow adding a border to an element by just adding a border-width. 403 | * 404 | * By default, the way the browser specifies that an element should have no 405 | * border is by setting it's border-style to `none` in the user-agent 406 | * stylesheet. 407 | * 408 | * In order to easily add borders to elements by just setting the `border-width` 409 | * property, we change the default border-style for all elements to `solid`, and 410 | * use border-width to hide them instead. This way our `border` utilities only 411 | * need to set the `border-width` property instead of the entire `border` 412 | * shorthand, making our border utilities much more straightforward to compose. 413 | * 414 | * https://github.com/tailwindcss/tailwindcss/pull/116 415 | */ 416 | 417 | *, 418 | ::before, 419 | ::after { 420 | box-sizing: border-box; 421 | /* 1 */ 422 | border-width: 0; 423 | /* 2 */ 424 | border-style: solid; 425 | /* 2 */ 426 | border-color: currentColor; 427 | /* 2 */ 428 | } 429 | 430 | /* 431 | * Ensure horizontal rules are visible by default 432 | */ 433 | 434 | hr { 435 | border-top-width: 1px; 436 | } 437 | 438 | /** 439 | * Undo the `border-style: none` reset that Normalize applies to images so that 440 | * our `border-{width}` utilities have the expected effect. 441 | * 442 | * The Normalize reset is unnecessary for us since we default the border-width 443 | * to 0 on all elements. 444 | * 445 | * https://github.com/tailwindcss/tailwindcss/issues/362 446 | */ 447 | 448 | img { 449 | border-style: solid; 450 | } 451 | 452 | textarea { 453 | resize: vertical; 454 | } 455 | 456 | input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { 457 | opacity: 1; 458 | color: #9ca3af; 459 | } 460 | 461 | input:-ms-input-placeholder, textarea:-ms-input-placeholder { 462 | opacity: 1; 463 | color: #9ca3af; 464 | } 465 | 466 | input::placeholder, 467 | textarea::placeholder { 468 | opacity: 1; 469 | color: #9ca3af; 470 | } 471 | 472 | button, 473 | [role="button"] { 474 | cursor: pointer; 475 | } 476 | 477 | /** 478 | * Override legacy focus reset from Normalize with modern Firefox focus styles. 479 | * 480 | * This is actually an improvement over the new defaults in Firefox in our testing, 481 | * as it triggers the better focus styles even for links, which still use a dotted 482 | * outline in Firefox by default. 483 | */ 484 | 485 | :-moz-focusring { 486 | outline: auto; 487 | } 488 | 489 | table { 490 | border-collapse: collapse; 491 | } 492 | 493 | h1, 494 | h2, 495 | h3, 496 | h4, 497 | h5, 498 | h6 { 499 | font-size: inherit; 500 | font-weight: inherit; 501 | } 502 | 503 | /** 504 | * Reset links to optimize for opt-in styling instead of 505 | * opt-out. 506 | */ 507 | 508 | a { 509 | color: inherit; 510 | text-decoration: inherit; 511 | } 512 | 513 | /** 514 | * Reset form element properties that are easy to forget to 515 | * style explicitly so you don't inadvertently introduce 516 | * styles that deviate from your design system. These styles 517 | * supplement a partial reset that is already applied by 518 | * normalize.css. 519 | */ 520 | 521 | button, 522 | input, 523 | optgroup, 524 | select, 525 | textarea { 526 | padding: 0; 527 | line-height: inherit; 528 | color: inherit; 529 | } 530 | 531 | /** 532 | * Use the configured 'mono' font family for elements that 533 | * are expected to be rendered with a monospace font, falling 534 | * back to the system monospace stack if there is no configured 535 | * 'mono' font family. 536 | */ 537 | 538 | pre, 539 | code, 540 | kbd, 541 | samp { 542 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 543 | } 544 | 545 | /** 546 | * 1. Make replaced elements `display: block` by default as that's 547 | * the behavior you want almost all of the time. Inspired by 548 | * CSS Remedy, with `svg` added as well. 549 | * 550 | * https://github.com/mozdevs/cssremedy/issues/14 551 | * 552 | * 2. Add `vertical-align: middle` to align replaced elements more 553 | * sensibly by default when overriding `display` by adding a 554 | * utility like `inline`. 555 | * 556 | * This can trigger a poorly considered linting error in some 557 | * tools but is included by design. 558 | * 559 | * https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210 560 | */ 561 | 562 | img, 563 | svg, 564 | video, 565 | canvas, 566 | audio, 567 | iframe, 568 | embed, 569 | object { 570 | display: block; 571 | /* 1 */ 572 | vertical-align: middle; 573 | /* 2 */ 574 | } 575 | 576 | /** 577 | * Constrain images and videos to the parent width and preserve 578 | * their intrinsic aspect ratio. 579 | * 580 | * https://github.com/mozdevs/cssremedy/issues/14 581 | */ 582 | 583 | img, 584 | video { 585 | max-width: 100%; 586 | height: auto; 587 | } 588 | 589 | /** 590 | * Ensure the default browser behavior of the `hidden` attribute. 591 | */ 592 | 593 | [hidden] { 594 | display: none; 595 | } 596 | 597 | *, ::before, ::after { 598 | --tw-translate-x: 0; 599 | --tw-translate-y: 0; 600 | --tw-rotate: 0; 601 | --tw-skew-x: 0; 602 | --tw-skew-y: 0; 603 | --tw-scale-x: 1; 604 | --tw-scale-y: 1; 605 | --tw-transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); 606 | --tw-border-opacity: 1; 607 | border-color: rgba(229, 231, 235, var(--tw-border-opacity)); 608 | --tw-ring-offset-shadow: 0 0 #0000; 609 | --tw-ring-shadow: 0 0 #0000; 610 | --tw-shadow: 0 0 #0000; 611 | } 612 | 613 | html, body{ 614 | font-family: 'Oswald', sans-serif; 615 | overflow-x: hidden; 616 | height: 100%; 617 | width: 100%; 618 | overflow-x: hidden; 619 | } 620 | 621 | .container { 622 | width: 100%; 623 | } 624 | 625 | @media (min-width: 640px) { 626 | .container { 627 | max-width: 640px; 628 | } 629 | } 630 | 631 | @media (min-width: 768px) { 632 | .container { 633 | max-width: 768px; 634 | } 635 | } 636 | 637 | @media (min-width: 1024px) { 638 | .container { 639 | max-width: 1024px; 640 | } 641 | } 642 | 643 | @media (min-width: 1280px) { 644 | .container { 645 | max-width: 1280px; 646 | } 647 | } 648 | 649 | @media (min-width: 1536px) { 650 | .container { 651 | max-width: 1536px; 652 | } 653 | } 654 | 655 | .invisible { 656 | visibility: hidden; 657 | } 658 | 659 | .absolute { 660 | position: absolute; 661 | } 662 | 663 | .relative { 664 | position: relative; 665 | } 666 | 667 | .inset-0 { 668 | top: 0px; 669 | right: 0px; 670 | bottom: 0px; 671 | left: 0px; 672 | } 673 | 674 | .right-0 { 675 | right: 0px; 676 | } 677 | 678 | .top-0 { 679 | top: 0px; 680 | } 681 | 682 | .bottom-14 { 683 | bottom: 3.5rem; 684 | } 685 | 686 | .left-1\/2 { 687 | left: 50%; 688 | } 689 | 690 | .top-14 { 691 | top: 3.5rem; 692 | } 693 | 694 | .left-14 { 695 | left: 3.5rem; 696 | } 697 | 698 | .z-20 { 699 | z-index: 20; 700 | } 701 | 702 | .mx-auto { 703 | margin-left: auto; 704 | margin-right: auto; 705 | } 706 | 707 | .mx-4 { 708 | margin-left: 1rem; 709 | margin-right: 1rem; 710 | } 711 | 712 | .mt-6 { 713 | margin-top: 1.5rem; 714 | } 715 | 716 | .mt-4 { 717 | margin-top: 1rem; 718 | } 719 | 720 | .ml-5 { 721 | margin-left: 1.25rem; 722 | } 723 | 724 | .mb-8 { 725 | margin-bottom: 2rem; 726 | } 727 | 728 | .mt-5 { 729 | margin-top: 1.25rem; 730 | } 731 | 732 | .mr-4 { 733 | margin-right: 1rem; 734 | } 735 | 736 | .mt-10 { 737 | margin-top: 2.5rem; 738 | } 739 | 740 | .mb-2 { 741 | margin-bottom: 0.5rem; 742 | } 743 | 744 | .block { 745 | display: block; 746 | } 747 | 748 | .inline-block { 749 | display: inline-block; 750 | } 751 | 752 | .flex { 753 | display: flex; 754 | } 755 | 756 | .grid { 757 | display: grid; 758 | } 759 | 760 | .hidden { 761 | display: none; 762 | } 763 | 764 | .h-6 { 765 | height: 1.5rem; 766 | } 767 | 768 | .h-96 { 769 | height: 24rem; 770 | } 771 | 772 | .h-3\/4 { 773 | height: 75%; 774 | } 775 | 776 | .h-\[500px\] { 777 | height: 500px; 778 | } 779 | 780 | .h-full { 781 | height: 100%; 782 | } 783 | 784 | .h-\[530px\] { 785 | height: 530px; 786 | } 787 | 788 | .h-10 { 789 | height: 2.5rem; 790 | } 791 | 792 | .h-1\/2 { 793 | height: 50%; 794 | } 795 | 796 | .h-11 { 797 | height: 2.75rem; 798 | } 799 | 800 | .max-h-36 { 801 | max-height: 9rem; 802 | } 803 | 804 | .max-h-64 { 805 | max-height: 16rem; 806 | } 807 | 808 | .max-h-72 { 809 | max-height: 18rem; 810 | } 811 | 812 | .min-h-\[200px\] { 813 | min-height: 200px; 814 | } 815 | 816 | .min-h-\[100px\] { 817 | min-height: 100px; 818 | } 819 | 820 | .min-h-\[50px\] { 821 | min-height: 50px; 822 | } 823 | 824 | .w-10 { 825 | width: 2.5rem; 826 | } 827 | 828 | .w-6 { 829 | width: 1.5rem; 830 | } 831 | 832 | .w-full { 833 | width: 100%; 834 | } 835 | 836 | .w-1\/2 { 837 | width: 50%; 838 | } 839 | 840 | .w-64 { 841 | width: 16rem; 842 | } 843 | 844 | .w-\[700px\] { 845 | width: 700px; 846 | } 847 | 848 | .w-11 { 849 | width: 2.75rem; 850 | } 851 | 852 | .w-16 { 853 | width: 4rem; 854 | } 855 | 856 | .w-1\/4 { 857 | width: 25%; 858 | } 859 | 860 | .w-48 { 861 | width: 12rem; 862 | } 863 | 864 | .w-96 { 865 | width: 24rem; 866 | } 867 | 868 | .w-1\/3 { 869 | width: 33.333333%; 870 | } 871 | 872 | .w-56 { 873 | width: 14rem; 874 | } 875 | 876 | .min-w-\[450px\] { 877 | min-width: 450px; 878 | } 879 | 880 | .flex-1 { 881 | flex: 1 1 0%; 882 | } 883 | 884 | .-translate-x-1\/2 { 885 | --tw-translate-x: -50%; 886 | -webkit-transform: var(--tw-transform); 887 | transform: var(--tw-transform); 888 | } 889 | 890 | .transform { 891 | -webkit-transform: var(--tw-transform); 892 | transform: var(--tw-transform); 893 | } 894 | 895 | @-webkit-keyframes bounce { 896 | 0%, 100% { 897 | -webkit-transform: translateY(-25%); 898 | transform: translateY(-25%); 899 | -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1); 900 | animation-timing-function: cubic-bezier(0.8,0,1,1); 901 | } 902 | 903 | 50% { 904 | -webkit-transform: none; 905 | transform: none; 906 | -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1); 907 | animation-timing-function: cubic-bezier(0,0,0.2,1); 908 | } 909 | } 910 | 911 | @keyframes bounce { 912 | 0%, 100% { 913 | -webkit-transform: translateY(-25%); 914 | transform: translateY(-25%); 915 | -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1); 916 | animation-timing-function: cubic-bezier(0.8,0,1,1); 917 | } 918 | 919 | 50% { 920 | -webkit-transform: none; 921 | transform: none; 922 | -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1); 923 | animation-timing-function: cubic-bezier(0,0,0.2,1); 924 | } 925 | } 926 | 927 | .animate-bounce { 928 | -webkit-animation: bounce 1s infinite; 929 | animation: bounce 1s infinite; 930 | } 931 | 932 | .cursor-pointer { 933 | cursor: pointer; 934 | } 935 | 936 | .select-none { 937 | -webkit-user-select: none; 938 | -ms-user-select: none; 939 | user-select: none; 940 | } 941 | 942 | .appearance-none { 943 | -webkit-appearance: none; 944 | appearance: none; 945 | } 946 | 947 | .grid-cols-2 { 948 | grid-template-columns: repeat(2, minmax(0, 1fr)); 949 | } 950 | 951 | .flex-col { 952 | flex-direction: column; 953 | } 954 | 955 | .items-center { 956 | align-items: center; 957 | } 958 | 959 | .justify-center { 960 | justify-content: center; 961 | } 962 | 963 | .justify-between { 964 | justify-content: space-between; 965 | } 966 | 967 | .gap-5 { 968 | gap: 1.25rem; 969 | } 970 | 971 | .gap-x-4 { 972 | -webkit-column-gap: 1rem; 973 | column-gap: 1rem; 974 | } 975 | 976 | .gap-y-1 { 977 | row-gap: 0.25rem; 978 | } 979 | 980 | .gap-x-1 { 981 | -webkit-column-gap: 0.25rem; 982 | column-gap: 0.25rem; 983 | } 984 | 985 | .gap-x-3 { 986 | -webkit-column-gap: 0.75rem; 987 | column-gap: 0.75rem; 988 | } 989 | 990 | .gap-y-5 { 991 | row-gap: 1.25rem; 992 | } 993 | 994 | .gap-x-2 { 995 | -webkit-column-gap: 0.5rem; 996 | column-gap: 0.5rem; 997 | } 998 | 999 | .overflow-hidden { 1000 | overflow: hidden; 1001 | } 1002 | 1003 | .overflow-x-hidden { 1004 | overflow-x: hidden; 1005 | } 1006 | 1007 | .overflow-y-scroll { 1008 | overflow-y: scroll; 1009 | } 1010 | 1011 | .whitespace-nowrap { 1012 | white-space: nowrap; 1013 | } 1014 | 1015 | .rounded-full { 1016 | border-radius: 9999px; 1017 | } 1018 | 1019 | .rounded-lg { 1020 | border-radius: 0.5rem; 1021 | } 1022 | 1023 | .rounded-2xl { 1024 | border-radius: 1rem; 1025 | } 1026 | 1027 | .rounded-md { 1028 | border-radius: 0.375rem; 1029 | } 1030 | 1031 | .rounded-t-lg { 1032 | border-top-left-radius: 0.5rem; 1033 | border-top-right-radius: 0.5rem; 1034 | } 1035 | 1036 | .rounded-b-lg { 1037 | border-bottom-right-radius: 0.5rem; 1038 | border-bottom-left-radius: 0.5rem; 1039 | } 1040 | 1041 | .border-4 { 1042 | border-width: 4px; 1043 | } 1044 | 1045 | .border-2 { 1046 | border-width: 2px; 1047 | } 1048 | 1049 | .border-gray-300 { 1050 | --tw-border-opacity: 1; 1051 | border-color: rgba(209, 213, 219, var(--tw-border-opacity)); 1052 | } 1053 | 1054 | .bg-white { 1055 | --tw-bg-opacity: 1; 1056 | background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); 1057 | } 1058 | 1059 | .bg-gray-300 { 1060 | --tw-bg-opacity: 1; 1061 | background-color: rgba(209, 213, 219, var(--tw-bg-opacity)); 1062 | } 1063 | 1064 | .bg-gray-900 { 1065 | --tw-bg-opacity: 1; 1066 | background-color: rgba(17, 24, 39, var(--tw-bg-opacity)); 1067 | } 1068 | 1069 | .bg-black { 1070 | --tw-bg-opacity: 1; 1071 | background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); 1072 | } 1073 | 1074 | .bg-purple-300 { 1075 | --tw-bg-opacity: 1; 1076 | background-color: rgba(196, 181, 253, var(--tw-bg-opacity)); 1077 | } 1078 | 1079 | .bg-gray-100 { 1080 | --tw-bg-opacity: 1; 1081 | background-color: rgba(243, 244, 246, var(--tw-bg-opacity)); 1082 | } 1083 | 1084 | .bg-red-500 { 1085 | --tw-bg-opacity: 1; 1086 | background-color: rgba(239, 68, 68, var(--tw-bg-opacity)); 1087 | } 1088 | 1089 | .bg-opacity-70 { 1090 | --tw-bg-opacity: 0.7; 1091 | } 1092 | 1093 | .object-cover { 1094 | object-fit: cover; 1095 | } 1096 | 1097 | .object-center { 1098 | object-position: center; 1099 | } 1100 | 1101 | .p-3 { 1102 | padding: 0.75rem; 1103 | } 1104 | 1105 | .py-1 { 1106 | padding-top: 0.25rem; 1107 | padding-bottom: 0.25rem; 1108 | } 1109 | 1110 | .px-4 { 1111 | padding-left: 1rem; 1112 | padding-right: 1rem; 1113 | } 1114 | 1115 | .py-3 { 1116 | padding-top: 0.75rem; 1117 | padding-bottom: 0.75rem; 1118 | } 1119 | 1120 | .px-8 { 1121 | padding-left: 2rem; 1122 | padding-right: 2rem; 1123 | } 1124 | 1125 | .px-3 { 1126 | padding-left: 0.75rem; 1127 | padding-right: 0.75rem; 1128 | } 1129 | 1130 | .py-7 { 1131 | padding-top: 1.75rem; 1132 | padding-bottom: 1.75rem; 1133 | } 1134 | 1135 | .px-5 { 1136 | padding-left: 1.25rem; 1137 | padding-right: 1.25rem; 1138 | } 1139 | 1140 | .px-48 { 1141 | padding-left: 12rem; 1142 | padding-right: 12rem; 1143 | } 1144 | 1145 | .py-4 { 1146 | padding-top: 1rem; 1147 | padding-bottom: 1rem; 1148 | } 1149 | 1150 | .px-1 { 1151 | padding-left: 0.25rem; 1152 | padding-right: 0.25rem; 1153 | } 1154 | 1155 | .py-2 { 1156 | padding-top: 0.5rem; 1157 | padding-bottom: 0.5rem; 1158 | } 1159 | 1160 | .pl-9 { 1161 | padding-left: 2.25rem; 1162 | } 1163 | 1164 | .pl-3 { 1165 | padding-left: 0.75rem; 1166 | } 1167 | 1168 | .pr-6 { 1169 | padding-right: 1.5rem; 1170 | } 1171 | 1172 | .pt-5 { 1173 | padding-top: 1.25rem; 1174 | } 1175 | 1176 | .pt-\[6px\] { 1177 | padding-top: 6px; 1178 | } 1179 | 1180 | .pt-7 { 1181 | padding-top: 1.75rem; 1182 | } 1183 | 1184 | .pb-9 { 1185 | padding-bottom: 2.25rem; 1186 | } 1187 | 1188 | .pt-4 { 1189 | padding-top: 1rem; 1190 | } 1191 | 1192 | .pt-2 { 1193 | padding-top: 0.5rem; 1194 | } 1195 | 1196 | .pr-16 { 1197 | padding-right: 4rem; 1198 | } 1199 | 1200 | .pr-10 { 1201 | padding-right: 2.5rem; 1202 | } 1203 | 1204 | .pb-5 { 1205 | padding-bottom: 1.25rem; 1206 | } 1207 | 1208 | .pb-2 { 1209 | padding-bottom: 0.5rem; 1210 | } 1211 | 1212 | .pl-5 { 1213 | padding-left: 1.25rem; 1214 | } 1215 | 1216 | .align-middle { 1217 | vertical-align: middle; 1218 | } 1219 | 1220 | .text-2xl { 1221 | font-size: 1.5rem; 1222 | line-height: 2rem; 1223 | } 1224 | 1225 | .text-lg { 1226 | font-size: 1.125rem; 1227 | line-height: 1.75rem; 1228 | } 1229 | 1230 | .text-xs { 1231 | font-size: 0.75rem; 1232 | line-height: 1rem; 1233 | } 1234 | 1235 | .text-sm { 1236 | font-size: 0.875rem; 1237 | line-height: 1.25rem; 1238 | } 1239 | 1240 | .font-semibold { 1241 | font-weight: 600; 1242 | } 1243 | 1244 | .font-light { 1245 | font-weight: 300; 1246 | } 1247 | 1248 | .font-bold { 1249 | font-weight: 700; 1250 | } 1251 | 1252 | .uppercase { 1253 | text-transform: uppercase; 1254 | } 1255 | 1256 | .tracking-wider { 1257 | letter-spacing: 0.05em; 1258 | } 1259 | 1260 | .text-gray-600 { 1261 | --tw-text-opacity: 1; 1262 | color: rgba(75, 85, 99, var(--tw-text-opacity)); 1263 | } 1264 | 1265 | .text-gray-500 { 1266 | --tw-text-opacity: 1; 1267 | color: rgba(107, 114, 128, var(--tw-text-opacity)); 1268 | } 1269 | 1270 | .text-yellow-300 { 1271 | --tw-text-opacity: 1; 1272 | color: rgba(252, 211, 77, var(--tw-text-opacity)); 1273 | } 1274 | 1275 | .text-gray-400 { 1276 | --tw-text-opacity: 1; 1277 | color: rgba(156, 163, 175, var(--tw-text-opacity)); 1278 | } 1279 | 1280 | .text-white { 1281 | --tw-text-opacity: 1; 1282 | color: rgba(255, 255, 255, var(--tw-text-opacity)); 1283 | } 1284 | 1285 | .shadow-md { 1286 | --tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); 1287 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 1288 | } 1289 | 1290 | .transition { 1291 | transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, -webkit-transform, -webkit-filter, -webkit-backdrop-filter; 1292 | transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; 1293 | transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-transform, -webkit-filter, -webkit-backdrop-filter; 1294 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 1295 | transition-duration: 150ms; 1296 | } 1297 | 1298 | .transition-colors { 1299 | transition-property: background-color, border-color, color, fill, stroke; 1300 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 1301 | transition-duration: 150ms; 1302 | } 1303 | 1304 | .duration-200 { 1305 | transition-duration: 200ms; 1306 | } 1307 | 1308 | .duration-1000 { 1309 | transition-duration: 1000ms; 1310 | } 1311 | 1312 | .duration-300 { 1313 | transition-duration: 300ms; 1314 | } 1315 | 1316 | .duration-500 { 1317 | transition-duration: 500ms; 1318 | } 1319 | 1320 | .ease-in { 1321 | transition-timing-function: cubic-bezier(0.4, 0, 1, 1); 1322 | } 1323 | 1324 | .ease-in-out { 1325 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 1326 | } 1327 | 1328 | @import url("https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css"); 1329 | 1330 | @import url("https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css"); 1331 | 1332 | @import url('https://fonts.googleapis.com/css2?family=Oswald:wght@200;300;400;500;600;700&display=swap'); 1333 | 1334 | .toggle-checkbox:checked { 1335 | @apply: right-0 border-green-400; 1336 | right: 0; 1337 | border-color: #68D391; 1338 | } 1339 | 1340 | .toggle-checkbox:checked + .toggle-label { 1341 | @apply: bg-green-400; 1342 | background-color: #68D391; 1343 | } 1344 | 1345 | .category-active { 1346 | border-radius: 1rem; 1347 | --tw-bg-opacity: 1; 1348 | background-color: rgba(254, 202, 202, var(--tw-bg-opacity)); 1349 | --tw-text-opacity: 1; 1350 | color: rgba(248, 113, 113, var(--tw-text-opacity)); 1351 | } 1352 | 1353 | .dark .category-active { 1354 | --tw-bg-opacity: 1; 1355 | background-color: rgba(248, 113, 113, var(--tw-bg-opacity)); 1356 | --tw-text-opacity: 1; 1357 | color: rgba(254, 226, 226, var(--tw-text-opacity)); 1358 | } 1359 | 1360 | .dark .category-active:hover { 1361 | --tw-text-opacity: 1; 1362 | color: rgba(254, 226, 226, var(--tw-text-opacity)); 1363 | } 1364 | 1365 | .slick-list{ 1366 | height: 100%; 1367 | } 1368 | 1369 | .hover\:bg-opacity-20:hover { 1370 | --tw-bg-opacity: 0.2; 1371 | } 1372 | 1373 | .hover\:text-red-400:hover { 1374 | --tw-text-opacity: 1; 1375 | color: rgba(248, 113, 113, var(--tw-text-opacity)); 1376 | } 1377 | 1378 | .hover\:text-opacity-90:hover { 1379 | --tw-text-opacity: 0.9; 1380 | } 1381 | 1382 | .focus\:outline-none:focus { 1383 | outline: 2px solid transparent; 1384 | outline-offset: 2px; 1385 | } 1386 | 1387 | .dark .dark\:border-black { 1388 | --tw-border-opacity: 1; 1389 | border-color: rgba(0, 0, 0, var(--tw-border-opacity)); 1390 | } 1391 | 1392 | .dark .dark\:border-gray-600 { 1393 | --tw-border-opacity: 1; 1394 | border-color: rgba(75, 85, 99, var(--tw-border-opacity)); 1395 | } 1396 | 1397 | .dark .dark\:border-gray-800 { 1398 | --tw-border-opacity: 1; 1399 | border-color: rgba(31, 41, 55, var(--tw-border-opacity)); 1400 | } 1401 | 1402 | .dark .dark\:border-gray-500 { 1403 | --tw-border-opacity: 1; 1404 | border-color: rgba(107, 114, 128, var(--tw-border-opacity)); 1405 | } 1406 | 1407 | .dark .dark\:border-gray-700 { 1408 | --tw-border-opacity: 1; 1409 | border-color: rgba(55, 65, 81, var(--tw-border-opacity)); 1410 | } 1411 | 1412 | .dark .dark\:bg-gray-900 { 1413 | --tw-bg-opacity: 1; 1414 | background-color: rgba(17, 24, 39, var(--tw-bg-opacity)); 1415 | } 1416 | 1417 | .dark .dark\:bg-gray-700 { 1418 | --tw-bg-opacity: 1; 1419 | background-color: rgba(55, 65, 81, var(--tw-bg-opacity)); 1420 | } 1421 | 1422 | .dark .dark\:bg-gray-500 { 1423 | --tw-bg-opacity: 1; 1424 | background-color: rgba(107, 114, 128, var(--tw-bg-opacity)); 1425 | } 1426 | 1427 | .dark .dark\:bg-gray-800 { 1428 | --tw-bg-opacity: 1; 1429 | background-color: rgba(31, 41, 55, var(--tw-bg-opacity)); 1430 | } 1431 | 1432 | .dark .dark\:text-white { 1433 | --tw-text-opacity: 1; 1434 | color: rgba(255, 255, 255, var(--tw-text-opacity)); 1435 | } 1436 | 1437 | .dark .dark\:text-gray-300 { 1438 | --tw-text-opacity: 1; 1439 | color: rgba(209, 213, 219, var(--tw-text-opacity)); 1440 | } 1441 | 1442 | .dark .dark\:text-gray-200 { 1443 | --tw-text-opacity: 1; 1444 | color: rgba(229, 231, 235, var(--tw-text-opacity)); 1445 | } 1446 | 1447 | .dark .dark\:text-gray-100 { 1448 | --tw-text-opacity: 1; 1449 | color: rgba(243, 244, 246, var(--tw-text-opacity)); 1450 | } 1451 | 1452 | @media (min-width: 768px) { 1453 | .md\:grid-cols-3 { 1454 | grid-template-columns: repeat(3, minmax(0, 1fr)); 1455 | } 1456 | } 1457 | 1458 | @media (min-width: 1024px) { 1459 | .lg\:flex { 1460 | display: flex; 1461 | } 1462 | 1463 | .lg\:w-96 { 1464 | width: 24rem; 1465 | } 1466 | 1467 | .lg\:grid-cols-4 { 1468 | grid-template-columns: repeat(4, minmax(0, 1fr)); 1469 | } 1470 | 1471 | .lg\:justify-between { 1472 | justify-content: space-between; 1473 | } 1474 | } 1475 | 1476 | @media (min-width: 1280px) { 1477 | .xl\:visible { 1478 | visibility: visible; 1479 | } 1480 | 1481 | .xl\:grid-cols-5 { 1482 | grid-template-columns: repeat(5, minmax(0, 1fr)); 1483 | } 1484 | } 1485 | 1486 | @media (min-width: 1536px) { 1487 | .\32xl\:grid-cols-6 { 1488 | grid-template-columns: repeat(6, minmax(0, 1fr)); 1489 | } 1490 | } --------------------------------------------------------------------------------