From 372f9f203e09677ae29eef822b67f8d05ca717b0 Mon Sep 17 00:00:00 2001 From: Nicholas Christopher Date: Sat, 18 May 2024 22:59:30 +0700 Subject: [PATCH 1/5] Team 3 --- .env.development | 1 + appsettings.js | 1 + components/BannerPromotions.tsx | 263 ++++++++++ components/DayButtons.tsx | 51 ++ components/Footer.tsx | 47 ++ components/LoadingSpinner.tsx | 28 ++ components/MovieBanner.tsx | 75 +++ components/MovieDate.tsx | 143 ++++++ components/MovieDetail.tsx | 40 ++ components/MovieList.tsx | 53 ++ components/NavBar.tsx | 102 ++++ components/Promotion.tsx | 128 +++++ components/SeatPicker.tsx | 60 +++ components/Title.tsx | 2 +- components/VerticalBanner.tsx | 70 +++ functions/moviesData.ts | 105 ++++ next.config.js | 109 ++--- package-lock.json | 828 ++++++++++++++++++++++++++++++-- package.json | 9 +- pages/api/be/[...apiGateway].ts | 4 +- pages/index.tsx | 21 +- pages/login/index.tsx | 133 +++++ pages/movies/index.tsx | 19 + pages/movies/schedule/[id].tsx | 60 +++ pages/promotion/index.tsx | 19 + pages/register/index.tsx | 229 +++++++++ pages/seat/[id].tsx | 85 ++++ pages/seat/transaction/[id].tsx | 64 +++ styles/globals.css | 4 +- 29 files changed, 2656 insertions(+), 97 deletions(-) create mode 100644 components/BannerPromotions.tsx create mode 100644 components/DayButtons.tsx create mode 100644 components/Footer.tsx create mode 100644 components/LoadingSpinner.tsx create mode 100644 components/MovieBanner.tsx create mode 100644 components/MovieDate.tsx create mode 100644 components/MovieDetail.tsx create mode 100644 components/MovieList.tsx create mode 100644 components/NavBar.tsx create mode 100644 components/Promotion.tsx create mode 100644 components/SeatPicker.tsx create mode 100644 components/VerticalBanner.tsx create mode 100644 functions/moviesData.ts create mode 100644 pages/login/index.tsx create mode 100644 pages/movies/index.tsx create mode 100644 pages/movies/schedule/[id].tsx create mode 100644 pages/promotion/index.tsx create mode 100644 pages/register/index.tsx create mode 100644 pages/seat/[id].tsx create mode 100644 pages/seat/transaction/[id].tsx diff --git a/.env.development b/.env.development index 58ed8bd..8924859 100644 --- a/.env.development +++ b/.env.development @@ -4,3 +4,4 @@ BACKEND_API_HOST=https://demo.duendesoftware.com OIDC_ISSUER=https://demo.duendesoftware.com OIDC_CLIENT_ID=interactive.public.short OIDC_SCOPE=openid profile email api offline_access +CGV_API=https://m.cgv.id diff --git a/appsettings.js b/appsettings.js index ebd662f..4718d44 100644 --- a/appsettings.js +++ b/appsettings.js @@ -3,4 +3,5 @@ module.exports = { oidcIssuer: process.env['OIDC_ISSUER'] ?? '', oidcClientId: process.env['OIDC_CLIENT_ID'] ?? '', oidcScope: process.env['OIDC_SCOPE'] ?? '', + cgvapi: process.env['CGV_API'] ?? '', }; diff --git a/components/BannerPromotions.tsx b/components/BannerPromotions.tsx new file mode 100644 index 0000000..b29ef1c --- /dev/null +++ b/components/BannerPromotions.tsx @@ -0,0 +1,263 @@ +// import Link from 'next/link'; +// import React, { useState } from 'react'; + +// interface Promotion { +// id: number; +// title: string; +// description: string; +// imageUrl: string; +// } + +// const promotions: Promotion[] = [ +// { +// id: 1, +// title: 'Promo 1', +// description: 'Diskon hingga 50% untuk produk tertentu', +// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405171125514961.jpg', +// }, +// { +// id: 2, +// title: 'Promo 2', +// description: 'Gratis ongkir untuk pembelian di atas Rp100.000', +// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405100933495889.jpg', +// }, +// { +// id: 3, +// title: 'Promo 3', +// description: 'Beli 1 gratis 1 untuk produk tertentu', +// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', +// }, +// { +// id: 4, +// title: 'Promo 4', +// description: 'Diskon hingga 30% untuk produk tertentu', +// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', +// }, +// { +// id: 5, +// title: 'Promo 5', +// description: 'Beli 2 gratis 1 untuk produk tertentu', +// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', +// }, +// { +// id: 6, +// title: 'Promo 6', +// description: 'Cashback hingga 20% untuk produk tertentu', +// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', +// }, +// ]; + +// const BannerPromotions: React.FC = () => { +// const [startIndex, setStartIndex] = useState(0); + +// const handleNext = () => { +// setStartIndex((prevIndex) => { +// const remainingItems = promotions.length - (prevIndex + 2); +// const newIndex = prevIndex + 1; +// return remainingItems >= 0 ? newIndex : prevIndex; +// }); +// }; + +// const handlePrev = () => { +// setStartIndex((prevIndex) => Math.max(prevIndex - 1, 0)); +// }; + +// return ( +//
+//
+//

PROMOTION

+// View All +//
+//
+//
+// {promotions.slice(startIndex, startIndex + 2 ).map((promotion) => ( +//
+//
+// {promotion.title} +//
+//

{promotion.title}

+//

{promotion.description}

+//
+//
+//
+// ))} +//
+// {startIndex > 0 && ( +// +// )} +// {startIndex < promotions.length - 2 && ( +// +// )} +//
+//
+// ); +// }; + +// export default BannerPromotions; + +// components/PromotionBanner.js +import { useState } from 'react'; +import { CSSTransition, TransitionGroup } from 'react-transition-group'; +import { createUseStyles } from 'react-jss'; + +const promotions = [ + { + id: 1, + title: 'Promo 1', + description: 'Diskon hingga 50% untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405171125514961.jpg', + }, + { + id: 2, + title: 'Promo 2', + description: 'Gratis ongkir untuk pembelian di atas Rp100.000', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405100933495889.jpg', + }, + { + id: 3, + title: 'Promo 3', + description: 'Beli 1 gratis 1 untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', + }, + { + id: 4, + title: 'Promo 4', + description: 'Diskon hingga 30% untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', + }, + { + id: 5, + title: 'Promo 5', + description: 'Beli 2 gratis 1 untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', + }, + { + id: 6, + title: 'Promo 6', + description: 'Cashback hingga 20% untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', + }, +]; + +const useStyles = createUseStyles({ + slideEnter: { + opacity: 0, + transform: 'translateX(100%)', + }, + slideEnterActive: { + opacity: 1, + transform: 'translateX(0)', + transition: 'opacity 500ms, transform 500ms', + }, + slideExit: { + opacity: 1, + transform: 'translateX(0)', + }, + slideExitActive: { + opacity: 0, + transform: 'translateX(-100%)', + transition: 'opacity 500ms, transform 500ms', + }, +}); + +const BannerPromotions = () => { + const classes = useStyles(); + const [currentPromotionIndex, setCurrentPromotionIndex] = useState(0); + + const handlePrev = () => { + setCurrentPromotionIndex((prevIndex) => + prevIndex === 0 ? promotions.length - 1 : prevIndex - 1 + ); + }; + + const handleNext = () => { + setCurrentPromotionIndex((prevIndex) => + prevIndex === promotions.length - 1 ? 0 : prevIndex + 1 + ); + }; + + return ( +
+
+ +
+ + +
+ {promotions[currentPromotionIndex].title} +
+

+ {promotions[currentPromotionIndex].title} +

+

+ {promotions[currentPromotionIndex].description} +

+
+
+
+
+
+ +
+
+ ); +}; + +export default BannerPromotions; diff --git a/components/DayButtons.tsx b/components/DayButtons.tsx new file mode 100644 index 0000000..49081fa --- /dev/null +++ b/components/DayButtons.tsx @@ -0,0 +1,51 @@ +import React, { useState } from 'react'; + +const getNextFiveDays = (): Date[] => { + const days: Date[] = []; + const today = new Date(); + for (let i = 0; i < 5; i++) { + const date = new Date(today); + date.setDate(today.getDate() + i); + days.push(date); + } + return days; +}; + +const formatDate = (date: Date): string => { + const options: Intl.DateTimeFormatOptions = { weekday: 'short', month: 'short', day: 'numeric' }; + return date.toLocaleDateString(undefined, options); +}; + +interface DayButtonsProps { + onDateChange: (date: Date) => void; +} + +const DayButtons: React.FC = ({ onDateChange }) => { + const days = getNextFiveDays(); + const [selectedDay, setSelectedDay] = useState(days[0]); + + const handleButtonClick = (day: Date) => { + setSelectedDay(day); + onDateChange(day); // Panggil fungsi onDateChange saat tanggal dipilih + }; + + return ( +
+ {days.map((day, index) => ( + + ))} +
+ ); +}; + +export default DayButtons; diff --git a/components/Footer.tsx b/components/Footer.tsx new file mode 100644 index 0000000..5e27486 --- /dev/null +++ b/components/Footer.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import Link from 'next/link'; + +const Footer: React.FC = () => { + return ( +
+
+
+
+ + CGV Logo + +

CGV Cinemas Indonesia (CJ CGV Cinemas Indonesia)

+
+ +
+

Quick Links

+
    +
  • Home
  • +
  • Movies
  • +
  • Cinemas
  • +
  • Contact Us
  • +
+
+ +
+

Follow Us

+ +
+
+ +
+

© 2024 CGV Cinema. All rights reserved.

+
+
+
+ ); +}; + +export default Footer; diff --git a/components/LoadingSpinner.tsx b/components/LoadingSpinner.tsx new file mode 100644 index 0000000..aa3182b --- /dev/null +++ b/components/LoadingSpinner.tsx @@ -0,0 +1,28 @@ +import React, { useState, useEffect } from 'react'; + +const LoadingSpinner: React.FC = () => { + const [dots, setDots] = useState('.'); + + useEffect(() => { + const interval = setInterval(() => { + setDots(prevDots => (prevDots.length < 3 ? prevDots + '.' : '.')); + }, 350); + + return () => clearInterval(interval); + }, []); + + return ( +
+
+ + Loading{dots} +
+ Loading{dots} +
+ ); +}; + +export default LoadingSpinner; diff --git a/components/MovieBanner.tsx b/components/MovieBanner.tsx new file mode 100644 index 0000000..d2be3f3 --- /dev/null +++ b/components/MovieBanner.tsx @@ -0,0 +1,75 @@ +import React, { useState } from 'react'; +import Link from 'next/link'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons'; +import movies from '@/functions/moviesData'; + +const splitTitle = (title: string) => { + const words = title.split(' '); + if (words.length > 3) { + return [words.slice(0, 3).join(' '), words.slice(3).join(' ')]; + } + return [title]; +}; + +const MovieBanner: React.FC = () => { + const [currentPage, setCurrentPage] = useState(0); + + const nextPage = () => { + setCurrentPage((prev) => Math.min(prev + 1, movies.length - 4)); + }; + + const prevPage = () => { + setCurrentPage((prev) => Math.max(prev - 1, 0)); + }; + + const startIndex = currentPage; + const endIndex = startIndex + 4; + const visibleMovies = movies.slice(startIndex, endIndex); + + return ( +
+
+ {visibleMovies.map((movie) => ( +
+
+ {movie.title} +
+
+
+ {splitTitle(movie.title).map((line, index) => ( +

+ {line} +

+ ))} +
+ + + BOOK NOW + + +
+
+ ))} +
+ {currentPage > 0 && ( + + )} + {endIndex < movies.length && ( + + )} +
+ ); +}; + +export default MovieBanner; diff --git a/components/MovieDate.tsx b/components/MovieDate.tsx new file mode 100644 index 0000000..3c2c6b9 --- /dev/null +++ b/components/MovieDate.tsx @@ -0,0 +1,143 @@ +import React, { useState } from 'react'; +import { useRouter } from 'next/router'; +import DayButtons from '@/components/DayButtons'; + +interface Showtime { + time: string; +} + +interface Cinema { + name: string; + showtimes: Showtime[]; +} + +interface City { + name: string; + cinemas: Cinema[]; +} + +const cities: City[] = [ + { + name: 'Jakarta', + cinemas: [ + { + name: 'Grand Indonesia', + showtimes: [ + { time: '10:00' }, + { time: '12:00' }, + { time: '14:00' }, + ], + }, + { + name: 'Pacific Place', + showtimes: [ + { time: '11:00' }, + { time: '13:00' }, + { time: '15:00' }, + ], + }, + { + name: 'Central Park', + showtimes: [ + { time: '11:00' }, + { time: '13:00' }, + { time: '15:00' }, + ], + }, + ], + }, + { + name: 'Bandung', + cinemas: [ + { + name: 'Miko Mall', + showtimes: [ + { time: '09:00' }, + { time: '11:00' }, + { time: '13:00' }, + ], + }, + { + name: 'Paris Van Java', + showtimes: [ + { time: '10:30' }, + { time: '12:30' }, + { time: '14:30' }, + ], + }, + ], + }, +]; + +interface MovieDateProps { + movieId: number; +} + +const MovieDate: React.FC = ({ movieId }) => { + const router = useRouter(); + const [selectedCity, setSelectedCity] = useState(cities[0].name); + const [selectedDate, setSelectedDate] = useState(new Date()); + + const handleCityChange = (event: React.ChangeEvent) => { + setSelectedCity(event.target.value); + }; + + const handleDateChange = (date: Date) => { + setSelectedDate(date); + }; + + const selectedCityData = cities.find(city => city.name === selectedCity); + + const handleShowtimeClick = (cinemaName: string, showtime: string) => { + const encodedMovieId = encodeURIComponent(movieId.toString()); + const encodedCinemaName = encodeURIComponent(cinemaName); + const encodedShowtime = encodeURIComponent(showtime); + const encodedDate = encodeURIComponent(selectedDate.toISOString().split('T')[0]); + router.push(`/seat/${encodedMovieId}-${encodedCinemaName}-${encodedShowtime}-${encodedDate}`); + }; + + return ( +
+
+
+ + +
+
+ + + + {selectedCityData && selectedCityData.cinemas.map((cinema, cinemaIndex) => ( +
+

{cinema.name}

+
+ {cinema.showtimes.map((showtime, showtimeIndex) => ( + + ))} +
+
+ ))} +
+ ); +}; + +export default MovieDate; diff --git a/components/MovieDetail.tsx b/components/MovieDetail.tsx new file mode 100644 index 0000000..a2effcc --- /dev/null +++ b/components/MovieDetail.tsx @@ -0,0 +1,40 @@ +import React from 'react'; + +interface Movie { + title: string; + description: string; + posterUrl: string; + rating: string; + genre:string; + duration:number; +} + +const MovieDetail: React.FC<{ movie: Movie }> = ({ movie }) => { + return ( +
+
+ {movie.title} +
+
+

{movie.title}

+

{movie.description}

+
+ Rating: {movie.rating} +
+ Genre: {movie.genre} +
+ Duration: {movie.duration} Minutes +
+ +
+ +
+ ); +}; + +export default MovieDetail; diff --git a/components/MovieList.tsx b/components/MovieList.tsx new file mode 100644 index 0000000..b07ed50 --- /dev/null +++ b/components/MovieList.tsx @@ -0,0 +1,53 @@ + import React, { useState } from 'react'; + import Link from 'next/link'; + import movies from '@/functions/moviesData'; + + const genres: string[] = ['All', 'Action', 'Romance', 'Comedy', 'Horror']; + + const MovieList: React.FC = () => { + const [selectedGenre, setSelectedGenre] = useState('All'); + + const handleGenreChange = (event: React.ChangeEvent) => { + setSelectedGenre(event.target.value); + }; + + const filteredMovies = selectedGenre === 'All' + ? movies + : movies.filter(movie => movie.genre === selectedGenre); + + return ( +
+
+

Now Playing

+ + +
+
+ {filteredMovies.map(movie => ( + + ))} +
+
+ ); + }; + + export default MovieList; diff --git a/components/NavBar.tsx b/components/NavBar.tsx new file mode 100644 index 0000000..5850ace --- /dev/null +++ b/components/NavBar.tsx @@ -0,0 +1,102 @@ +import React, { useState, useEffect } from 'react'; +import Link from 'next/link'; +import { useSession, signIn, signOut } from 'next-auth/react'; +import Head from 'next/head'; + +const Navbar: React.FC = () => { + const { data: session } = useSession(); + const isLoggedIn = !!session?.user; + const [showDropdown, setShowDropdown] = useState(false); + + const handleLogout = async () => { + await signOut(); + }; + + const toggleDropdown = () => { + setShowDropdown(!showDropdown); + }; + + useEffect(() => { + const handleOutsideClick = (event: MouseEvent) => { + if (!event.target.closest('#user-menu-button') && !event.target.closest('#user-dropdown')) { + setShowDropdown(false); + } + }; + + if (showDropdown) { + document.addEventListener('mousedown', handleOutsideClick); + } else { + document.removeEventListener('mousedown', handleOutsideClick); + } + + return () => { + document.removeEventListener('mousedown', handleOutsideClick); + }; + }, [showDropdown]); + + return ( + + ); +}; + +export default Navbar; diff --git a/components/Promotion.tsx b/components/Promotion.tsx new file mode 100644 index 0000000..7ee5a30 --- /dev/null +++ b/components/Promotion.tsx @@ -0,0 +1,128 @@ +import { useState, useEffect } from 'react'; +import Image from 'next/image'; + +const imageUrls = [ + 'https://cdn.cgv.id/uploads_v2/banners/jOCPp-20240422094323.jpg', + 'https://cdn.cgv.id/uploads_v2/banners/5vcde-20240408151019.png', + 'https://cdn.cgv.id/uploads_v2/banners/rHKCS-20240516084600.jpg', + 'https://cdn.cgv.id/uploads_v2/banners/3Q7sM-20240516160347.jpg', + 'https://cdn.cgv.id/uploads_v2/banners/BTTyz-20240509152213.jpeg', +]; + +const Promotions: React.FC = () => { + const [currentSlide, setCurrentSlide] = useState(0); + const [resetTimer, setResetTimer] = useState(false); + + const nextSlide = () => { + setCurrentSlide((prevSlide) => (prevSlide + 1) % imageUrls.length); + setResetTimer(true); + }; + + const prevSlide = () => { + setCurrentSlide((prevSlide) => (prevSlide - 1 + imageUrls.length) % imageUrls.length); + setResetTimer(true); + }; + + useEffect(() => { + const interval = setInterval(() => { + nextSlide(); + if (resetTimer) { + clearInterval(interval); + setResetTimer(false); + } + }, 3000); + + return () => clearInterval(interval); + }, [currentSlide, resetTimer]); + + return ( + + ); +}; + +export default Promotions; diff --git a/components/SeatPicker.tsx b/components/SeatPicker.tsx new file mode 100644 index 0000000..8e11790 --- /dev/null +++ b/components/SeatPicker.tsx @@ -0,0 +1,60 @@ +import { useState } from 'react'; + +type SeatPickerProps = { + rows: number; + cols: number; + onSeatSelect: (selectedSeats: string[]) => void; +}; + +const SeatPicker: React.FC = ({ rows, cols, onSeatSelect }) => { + const [selectedSeats, setSelectedSeats] = useState>(new Set()); + + const toggleSeatSelection = (seatId: string) => { + setSelectedSeats((prev) => { + const newSelectedSeats = new Set(prev); + if (newSelectedSeats.has(seatId)) { + newSelectedSeats.delete(seatId); + } else { + newSelectedSeats.add(seatId); + } + return newSelectedSeats; + }); + + onSeatSelect(Array.from(selectedSeats)); + }; + + const generateSeatLabel = (row: number, col: number) => { + const rowLabel = String.fromCharCode(65 + row); + const colLabel = (col + 1).toString(); + return `${rowLabel}${colLabel}`; + }; + + return ( +
+
+ {Array.from({ length: rows }).map((_, row) => + Array.from({ length: cols }).map((_, col) => { + const seatId = generateSeatLabel(row, col); + const isSelected = selectedSeats.has(seatId); + return ( +
toggleSeatSelection(seatId)} + className={`w-10 h-10 flex items-center justify-center cursor-pointer + ${isSelected ? 'bg-green-500' : 'bg-gray-300'} + hover:bg-gray-400`} + > + {seatId} +
+ ); + }) + )} +
+
+ Selected Seats: {Array.from(selectedSeats).join(', ')} +
+
+ ); +}; + +export default SeatPicker; diff --git a/components/Title.tsx b/components/Title.tsx index 2fa4f9a..5c920a0 100644 --- a/components/Title.tsx +++ b/components/Title.tsx @@ -6,7 +6,7 @@ export const Title: React.FC<{ }> = ({ children }) => { return ( - {children.toString() + '- Accelist Next.js Starter'} + {children.toString() + ' - CGV Cinemas'} ); } diff --git a/components/VerticalBanner.tsx b/components/VerticalBanner.tsx new file mode 100644 index 0000000..f0e3684 --- /dev/null +++ b/components/VerticalBanner.tsx @@ -0,0 +1,70 @@ +import React, { useState } from "react"; + +interface Promotion { + id: number; + title: string; + description: string; + imageUrl: string; +} + +const VerticalBanner: React.FC = () => { + const [promotions] = useState([ + { + id: 1, + title: 'Promo 1', + description: 'Diskon hingga 50% untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405171125514961.jpg', + }, + { + id: 2, + title: 'Promo 2', + description: 'Gratis ongkir untuk pembelian di atas Rp100.000', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405100933495889.jpg', + }, + { + id: 3, + title: 'Promo 3', + description: 'Beli 1 gratis 1 untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', + }, + { + id: 4, + title: 'Promo 4', + description: 'Diskon hingga 30% untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', + }, + { + id: 5, + title: 'Promo 5', + description: 'Beli 2 gratis 1 untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', + }, + { + id: 6, + title: 'Promo 6', + description: 'Cashback hingga 20% untuk produk tertentu', + imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', + }, + ]); + + return ( +
+
+ Banner Promotion +
+
+ {promotions.map((promotion) => ( +
+ {promotion.title} +
+

{promotion.title}

+

{promotion.description}

+
+
+ ))} +
+
+ ); +}; + +export default VerticalBanner; diff --git a/functions/moviesData.ts b/functions/moviesData.ts new file mode 100644 index 0000000..283e85e --- /dev/null +++ b/functions/moviesData.ts @@ -0,0 +1,105 @@ +export interface Movie { + id: number; + title: string; + genre: string; + posterUrl: string; + rating: string; + description: string; + duration: number; + } + + const movies: Movie[] = [ + { + id: 1, + title: 'DO YOU SEE WHAT I SEE', + genre: 'Horror', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24006500.jpg?version=2', + rating: '17+', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 120 + }, + { + id: 2, + title: 'VINA: SEBELUM 7 HARI', + genre: 'Horror', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24010700.jpg?version=1', + rating: '17+', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 200 + }, + { + id: 3, + title: 'IF: IMAGINARY FRIENDS', + genre: 'Comedy', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24013100.jpg?version=1', + rating: 'SU', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 100 + }, + { + id: 4, + title: 'TAROT', + genre: 'Horror', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24013300.jpg?version=1', + rating: '17+', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 100 + }, + { + id: 5, + title: 'HOW TO MAKE MILLIONS BEFORE GRANDMA DIES', + genre: 'Comedy', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24012700.jpg?version=1', + rating: 'SU', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 100 + }, + { + id: 6, + title: 'KINGDOM OF THE PLANET OF THE APES', + genre: 'Action', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24013000.jpg?version=1', + rating: '13+', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 100 + }, + { + id: 7, + title: 'RASCAL DOES NOT DREAM OF A KNAPSACK KID', + genre: 'Romance', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24012300.jpg?version=1', + rating: '13+', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 100 + }, + { + id: 8, + title: 'THE ARCHITECTURE OF LOVE', + genre: 'Romance', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24011500.jpg?version=2', + rating: '13+', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 100 + }, + { + id: 9, + title: 'BADARAWUHI DI DESA PENARI', + genre: 'Horror', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24003200.jpg?version=3', + rating: '17+', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 100 + }, + { + id: 10, + title: 'THE ROUNDUP: PUNISHMENT', + genre: 'Action', + posterUrl: 'https://cdn.cgv.id/uploads_v2/movie/compressed/24011900.jpg?version=1', + rating: '13+', + description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', + duration: 100 + } + ]; + + export default movies; + \ No newline at end of file diff --git a/next.config.js b/next.config.js index 54aec74..1f94df2 100644 --- a/next.config.js +++ b/next.config.js @@ -1,67 +1,58 @@ -// https://securityheaders.com/ -function getSecurityHeaders(isProd) { - const headers = [ - { - key: 'X-Frame-Options', - value: 'SAMEORIGIN' - }, - { - key: 'X-Content-Type-Options', - value: 'nosniff' - }, +// next.config.js +module.exports = { + images: { + domains: ['cdn.cgv.id', 'dummyjson.com', 'vietguys.biz','encrypted-tbn0.gstatic.com','cgv.id'], + }, + async headers() { + return [ { - key: 'Referrer-Policy', - value: 'origin-when-cross-origin' + source: '/:path*', + headers: getSecurityHeaders(process.env.NODE_ENV === 'production'), }, + ] + }, + reactStrictMode: true, + productionBrowserSourceMaps: true, + serverRuntimeConfig: require('./appsettings'), + } + + function getSecurityHeaders(isProd) { + const headers = [ + { + key: 'X-Frame-Options', + value: 'SAMEORIGIN' + }, + { + key: 'X-Content-Type-Options', + value: 'nosniff' + }, + { + key: 'Referrer-Policy', + value: 'origin-when-cross-origin' + }, + { + key: 'Permissions-Policy', + value: 'accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()' + }, + { + key: 'X-XSS-Protection', + value: '1; mode=block' + } + ]; + + if (isProd) { + headers.push( { - // https://www.permissionspolicy.com/ - key: 'Permissions-Policy', - value: 'accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()' + key: 'Strict-Transport-Security', + value: 'max-age=31536000; includeSubDomains; preload' }, { - key: 'X-XSS-Protection', - value: '1; mode=block' + key: 'Content-Security-Policy', + value: `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'` } - ]; - - if (isProd) { - headers.push( - { - key: 'Strict-Transport-Security', - value: 'max-age=31536000' - }, - { - // https://report-uri.com/home/generate - // https://csp-evaluator.withgoogle.com/ - key: 'Content-Security-Policy', - value: `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self'; object-src 'none'` - } - ); + ); } - + return headers; -} - -const isProd = process.env['NODE_ENV'] === 'production'; - -/** @type { import('next').NextConfig } */ -const nextConfig = { - // add environment variables accessible via AppSettings here: - // visible only by server-side Next.js (secrets) - // if accessing variables required in browser-side code, use getServerSideProps - // https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props - serverRuntimeConfig: require('./appsettings'), - productionBrowserSourceMaps: true, - reactStrictMode: true, - async headers() { - return [ - { - // Apply these headers to all routes in your application. - source: '/:path*', - headers: getSecurityHeaders(isProd), - }, - ] - }, -}; - -module.exports = nextConfig; + } + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9344003..a106f08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,10 @@ "version": "0.1.0", "license": "Apache-2.0", "dependencies": { - "@fortawesome/fontawesome-svg-core": "6.4.0", + "@fortawesome/fontawesome-svg-core": "^6.4.0", + "@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-solid-svg-icons": "6.4.0", - "@fortawesome/react-fontawesome": "0.2.0", + "@fortawesome/react-fontawesome": "^0.2.0", "@hookform/error-message": "2.0.1", "@hookform/resolvers": "3.0.1", "antd": "5.4.0", @@ -23,8 +24,12 @@ "nprogress": "0.2.0", "openid-client": "5.4.0", "react": "18.2.0", + "react-datepicker": "^6.9.0", "react-dom": "18.2.0", "react-hook-form": "7.43.9", + "react-jss": "^10.10.0", + "react-select": "^5.8.0", + "react-transition-group": "^4.4.5", "swr": "2.1.2", "zod": "3.21.4" }, @@ -112,6 +117,123 @@ "react": ">=16.9.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dependencies": { + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/runtime": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", @@ -123,6 +245,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/types": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "dependencies": { + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@ctrl/tinycolor": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz", @@ -131,16 +266,137 @@ "node": ">=10" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, "node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz", + "integrity": "sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==", + "dependencies": { + "@emotion/memoize": "0.7.1" + } + }, + "node_modules/@emotion/is-prop-valid/node_modules/@emotion/memoize": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz", + "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==" + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/serialize/node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/serialize/node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, "node_modules/@emotion/unitless": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -197,6 +453,54 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz", + "integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==", + "dependencies": { + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz", + "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.15", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.15.tgz", + "integrity": "sha512-WKmfLkxTwCm09Dxq4LpjL3EPbZVSp5wvnap1jmculsfnzg2Ag/pCkP+OPyjE5dFMXqX97hsLIqJehboZ5XAHXw==", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@floating-ui/utils": "^0.2.0", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz", + "integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", + "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==" + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz", @@ -218,6 +522,27 @@ "node": ">=6" } }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.2.tgz", + "integrity": "sha512-zi5FNYdmKLnEc0jc0uuHH17kz/hfYTg4Uei0wMGzcoCL/4d3WM3u1VMc0iGGa31HuhV5i7ZK8ZlTCQrHqRHSGQ==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz", + "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@fortawesome/free-solid-svg-icons": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.0.tgz", @@ -704,28 +1029,38 @@ "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==", "dev": true }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { "version": "18.0.33", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.33.tgz", "integrity": "sha512-sHxzVxeanvQyQ1lr8NSHaj0kDzcNiGpILEVt69g9S31/7PfMvNCKLKcsHw4lYKjs3cGNJjXSP4mYzX43QlnjNA==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "node_modules/@types/semver": { "version": "7.3.13", @@ -1254,6 +1589,20 @@ "deep-equal": "^2.0.5" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1347,7 +1696,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -1471,6 +1819,14 @@ "node": ">=12" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1536,6 +1892,11 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -1552,6 +1913,21 @@ "toggle-selection": "^1.0.6" } }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -1584,6 +1960,25 @@ "node": ">= 8" } }, + "node_modules/css-jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/css-jss/-/css-jss-10.10.0.tgz", + "integrity": "sha512-YyMIS/LsSKEGXEaVJdjonWe18p4vXLo8CMA4FrW/kcaEyqdIGKCFXao31gbJddXEdIxSXFFURWrenBJPlKTgAA==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "^10.10.0", + "jss-preset-default": "^10.10.0" + } + }, + "node_modules/css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1742,6 +2137,15 @@ "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz", "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==" }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.356", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.356.tgz", @@ -1767,6 +2171,14 @@ "node": ">=10.13.0" } }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.21.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", @@ -1888,7 +2300,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -2499,6 +2910,11 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2598,8 +3014,7 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -2810,7 +3225,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -2887,6 +3301,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/http-proxy": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", @@ -2924,6 +3346,11 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/hyphenate-style-name": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.5.tgz", + "integrity": "sha512-fedL7PRwmeVkgyhu9hLeTBaI6wcGk7JGJswdaRsa5aUbkXI1kr1xZwTPBtaYPpwf56878iDek6VbVnuWMebJmw==" + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -2937,7 +3364,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3018,6 +3444,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -3074,7 +3505,6 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -3142,6 +3572,11 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" + }, "node_modules/is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -3412,6 +3847,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3444,6 +3884,158 @@ "json5": "lib/cli.js" } }, + "node_modules/jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", + "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", + "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-compose": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-compose/-/jss-plugin-compose-10.10.0.tgz", + "integrity": "sha512-F5kgtWpI2XfZ3Z8eP78tZEYFdgTIbpA/TMuX3a8vwrNolYtN1N4qJR/Ob0LAsqIwCMLojtxN7c7Oo/+Vz6THow==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", + "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-expand": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-expand/-/jss-plugin-expand-10.10.0.tgz", + "integrity": "sha512-ymT62W2OyDxBxr7A6JR87vVX9vTq2ep5jZLIdUSusfBIEENLdkkc0lL/Xaq8W9s3opUq7R0sZQpzRWELrfVYzA==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-extend": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-extend/-/jss-plugin-extend-10.10.0.tgz", + "integrity": "sha512-sKYrcMfr4xxigmIwqTjxNcHwXJIfvhvjTNxF+Tbc1NmNdyspGW47Ey6sGH8BcQ4FFQhLXctpWCQSpDwdNmXSwg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", + "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", + "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", + "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", + "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-rule-value-observable": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-observable/-/jss-plugin-rule-value-observable-10.10.0.tgz", + "integrity": "sha512-ZLMaYrR3QE+vD7nl3oNXuj79VZl9Kp8/u6A1IbTPDcuOu8b56cFdWRZNZ0vNr8jHewooEeq2doy8Oxtymr2ZPA==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "symbol-observable": "^1.2.0" + } + }, + "node_modules/jss-plugin-template": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-template/-/jss-plugin-template-10.10.0.tgz", + "integrity": "sha512-ocXZBIOJOA+jISPdsgkTs8wwpK6UbsvtZK5JI7VUggTD6LWKbtoxUzadd2TpfF+lEtlhUmMsCkTRNkITdPKa6w==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", + "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.10.0" + } + }, + "node_modules/jss-preset-default": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-preset-default/-/jss-preset-default-10.10.0.tgz", + "integrity": "sha512-GL175Wt2FGhjE+f+Y3aWh+JioL06/QWFgZp53CbNNq6ZkVU0TDplD8Bxm9KnkotAYn3FlplNqoW5CjyLXcoJ7Q==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "jss-plugin-camel-case": "10.10.0", + "jss-plugin-compose": "10.10.0", + "jss-plugin-default-unit": "10.10.0", + "jss-plugin-expand": "10.10.0", + "jss-plugin-extend": "10.10.0", + "jss-plugin-global": "10.10.0", + "jss-plugin-nested": "10.10.0", + "jss-plugin-props-sort": "10.10.0", + "jss-plugin-rule-value-function": "10.10.0", + "jss-plugin-rule-value-observable": "10.10.0", + "jss-plugin-template": "10.10.0", + "jss-plugin-vendor-prefixer": "10.10.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", @@ -3503,8 +4095,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/locate-path": { "version": "6.0.0", @@ -3585,6 +4176,11 @@ "node": ">=10" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4080,7 +4676,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -4088,6 +4683,23 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4118,14 +4730,12 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -4969,6 +5579,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-datepicker": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-6.9.0.tgz", + "integrity": "sha512-QTxuzeem7BUfVFWv+g5WuvzT0c5BPo+XTCNbMTZKSZQLU+cMMwSUHwspaxuIcDlwNcOH0tiJ+bh1fJ2yxOGYWA==", + "dependencies": { + "@floating-ui/react": "^0.26.2", + "clsx": "^2.1.0", + "date-fns": "^3.3.1", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.13.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, + "node_modules/react-datepicker/node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/react-display-name": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/react-display-name/-/react-display-name-0.2.5.tgz", + "integrity": "sha512-I+vcaK9t4+kypiSgaiVWAipqHRXYmZIuAiS8vzFvXHHXVigg/sMKwlRgLy6LH2i3rmP+0Vzfl5lFsFRwF1r3pg==" + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -5001,6 +5641,75 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/react-jss/-/react-jss-10.10.0.tgz", + "integrity": "sha512-WLiq84UYWqNBF6579/uprcIUnM1TSywYq6AIjKTTTG5ziJl9Uy+pwuvpN3apuyVwflMbD60PraeTKT7uWH9XEQ==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "@emotion/is-prop-valid": "^0.7.3", + "css-jss": "10.10.0", + "hoist-non-react-statics": "^3.2.0", + "is-in-browser": "^1.1.3", + "jss": "10.10.0", + "jss-preset-default": "10.10.0", + "prop-types": "^15.6.0", + "shallow-equal": "^1.2.0", + "theming": "^3.3.0", + "tiny-warning": "^1.0.2" + }, + "peerDependencies": { + "react": ">=16.8.6" + } + }, + "node_modules/react-onclickoutside": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.1.tgz", + "integrity": "sha512-LdrrxK/Yh9zbBQdFbMTXPp3dTSN9B+9YJQucdDu3JNKRrbdU+H+/TVONJoWtOwy4II8Sqf1y/DTI6w/vGPYW0w==", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" + }, + "peerDependencies": { + "react": "^15.5.x || ^16.x || ^17.x || ^18.x", + "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" + } + }, + "node_modules/react-select": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", + "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -5067,7 +5776,6 @@ "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, "dependencies": { "is-core-module": "^2.11.0", "path-parse": "^1.0.7", @@ -5084,7 +5792,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -5191,6 +5898,11 @@ "node": ">=10" } }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5250,6 +5962,14 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -5438,9 +6158,9 @@ } }, "node_modules/stylis": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz", - "integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, "node_modules/sucrase": { "version": "3.32.0", @@ -5503,7 +6223,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -5522,6 +6241,14 @@ "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/synckit": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", @@ -5538,6 +6265,11 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/tailwindcss": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.1.tgz", @@ -5633,6 +6365,23 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/theming": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/theming/-/theming-3.3.0.tgz", + "integrity": "sha512-u6l4qTJRDaWZsqa8JugaNt7Xd8PPl9+gonZaIe28vAhqgHMIG/DOyFPqiKN/gQLQYj05tHv+YQdNILL4zoiAVA==", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.8", + "react-display-name": "^0.2.4", + "tiny-warning": "^1.0.2" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.3" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -5672,6 +6421,19 @@ "globrex": "^0.1.2" } }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5843,6 +6605,19 @@ "punycode": "^2.1.0" } }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -5981,7 +6756,6 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, "engines": { "node": ">= 6" } diff --git a/package.json b/package.json index 3d75a9a..50b4837 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,10 @@ "typescript": "5.0.4" }, "dependencies": { - "@fortawesome/fontawesome-svg-core": "6.4.0", + "@fortawesome/fontawesome-svg-core": "^6.4.0", + "@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-solid-svg-icons": "6.4.0", - "@fortawesome/react-fontawesome": "0.2.0", + "@fortawesome/react-fontawesome": "^0.2.0", "@hookform/error-message": "2.0.1", "@hookform/resolvers": "3.0.1", "antd": "5.4.0", @@ -53,8 +54,12 @@ "nprogress": "0.2.0", "openid-client": "5.4.0", "react": "18.2.0", + "react-datepicker": "^6.9.0", "react-dom": "18.2.0", "react-hook-form": "7.43.9", + "react-jss": "^10.10.0", + "react-select": "^5.8.0", + "react-transition-group": "^4.4.5", "swr": "2.1.2", "zod": "3.21.4" } diff --git a/pages/api/be/[...apiGateway].ts b/pages/api/be/[...apiGateway].ts index 3dea920..97d748b 100644 --- a/pages/api/be/[...apiGateway].ts +++ b/pages/api/be/[...apiGateway].ts @@ -5,7 +5,7 @@ import { AppSettings } from '../../../functions/AppSettings'; // Great way to avoid using CORS and making API calls from HTTPS pages to back-end HTTP servers // Recommendation for projects in Kubernetes cluster: set target to Service DNS name instead of public DNS name const server = Proxy.createProxyServer({ - target: AppSettings.current.backendApiHost, + target: AppSettings.current.cgvapi, // changeOrigin to support name-based virtual hosting changeOrigin: true, xfwd: true, @@ -23,7 +23,7 @@ server.on('proxyReq', (proxyReq, req) => { } proxyReq.removeHeader('cookie'); // console.log(JSON.stringify(proxyReq.getHeaders(), null, 4)); - console.log('API Proxy:', req.url, '-->', AppSettings.current.backendApiHost + urlRewrite); + console.log('API Proxy:', req.url, '-->', AppSettings.current.cgvapi + urlRewrite); }); const apiGateway = async (req: NextApiRequest, res: NextApiResponse) => { diff --git a/pages/index.tsx b/pages/index.tsx index 6c0943a..cbab998 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,15 +1,28 @@ -import { WithDefaultLayout } from '../components/DefautLayout'; import { Title } from '../components/Title'; import { Page } from '../types/Page'; +import Navbar from '@/components/NavBar'; +import Promotions from '@/components/Promotion'; +import MovieBanner from '@/components/MovieBanner'; +import BannerPromotions from '@/components/BannerPromotions'; +import Footer from '@/components/Footer'; const IndexPage: Page = () => { return (
- Home - Hello World! + +
+ Home +
+ +

NOW SHOWING

+ + +
+ +
+
); } -IndexPage.layout = WithDefaultLayout; export default IndexPage; diff --git a/pages/login/index.tsx b/pages/login/index.tsx new file mode 100644 index 0000000..08e5c00 --- /dev/null +++ b/pages/login/index.tsx @@ -0,0 +1,133 @@ +import React, { useState } from 'react'; +import { useRouter } from 'next/router'; +import { signIn, useSession } from 'next-auth/react'; +import nProgress from 'nprogress'; +import Link from 'next/link'; +import Image from 'next/image'; +import { Title } from '../../components/Title'; +import Navbar from '@/components/NavBar'; +import Footer from '@/components/Footer'; + +const LoginPage = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const router = useRouter(); + const { data: session } = useSession(); + + if (session) { + router.push('/'); + return null; + } + + const handleLogin = async (e) => { + e.preventDefault(); + try { + const response = await fetch('/api/be/api/v1/Auth/Login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + email: email, + password: password, + phone: 0, + }) + }); + + if (response.ok) { + router.push('/'); + } else { + console.error('Login failed:', response.statusText); + } + } catch (error) { + console.error('Login failed:', error); + } + }; + + const handleOidcSignIn = () => { + nProgress.start(); + signIn('oidc'); + }; + + return ( +
+ +
+ Login +
+
+ + + CGV Logo + + +
+
+
+ + setEmail(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+
+ + setPassword(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+ + +
+

+ Don't have an account yet?{' '} + + Register Now + +

+
+ + Forgot password + +
+
+ +
+
+
+
+ ); +}; + +export default LoginPage; diff --git a/pages/movies/index.tsx b/pages/movies/index.tsx new file mode 100644 index 0000000..f5d95b8 --- /dev/null +++ b/pages/movies/index.tsx @@ -0,0 +1,19 @@ +import { Title } from '../../components/Title'; +import Navbar from '@/components/NavBar'; +import MovieList from '@/components/MovieList'; +import Footer from '@/components/Footer'; + +const IndexPage = () => { + return ( +
+ +
+ Movies + +
+
+
+ ); +} + +export default IndexPage; diff --git a/pages/movies/schedule/[id].tsx b/pages/movies/schedule/[id].tsx new file mode 100644 index 0000000..addd5e4 --- /dev/null +++ b/pages/movies/schedule/[id].tsx @@ -0,0 +1,60 @@ +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import Navbar from '@/components/NavBar'; +import MovieDetail from '@/components/MovieDetail'; +import DayButtons from '@/components/DayButtons'; +import MovieDate from '@/components/MovieDate'; +import moviesData, { Movie } from '@/functions/moviesData'; +import LoadingSpinner from '@/components/LoadingSpinner'; + +const ScheduleDetails: React.FC = () => { + const router = useRouter(); + const { id } = router.query; + + const [scheduleData, setScheduleData] = useState<{ id: string | string[] | undefined, movies: Movie[] } | null>(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchData = async () => { + try { + await new Promise(resolve => setTimeout(resolve, 1000)); + const data = { + id, + movies: moviesData.filter((movie: Movie) => movie.id === parseInt(id as string)) + }; + setScheduleData(data); + } catch (error) { + console.error('Error fetching schedule data:', error); + } finally { + setLoading(false); + } + }; + if (id) { + fetchData(); + } + }, [id]); + + if (loading) { + return + } + + if (!scheduleData || scheduleData.movies.length === 0) { + return
No schedule data found.
; + } + + return ( +
+ +

Schedule Details

+

ID: {scheduleData.id}

+ {scheduleData.movies.map((movie: Movie, index: number) => ( + + ))} + {scheduleData.movies.map((movie: Movie, index: number) => ( + + ))} +
+ ); +}; + +export default ScheduleDetails; diff --git a/pages/promotion/index.tsx b/pages/promotion/index.tsx new file mode 100644 index 0000000..8a608a8 --- /dev/null +++ b/pages/promotion/index.tsx @@ -0,0 +1,19 @@ +import { Title } from '../../components/Title'; +import Navbar from '@/components/NavBar'; +import VerticalBanner from '@/components/VerticalBanner'; +import Footer from '@/components/Footer'; + +const IndexPage = () => { + return ( +
+ +
+ Promotion + +
+
+
+ ); +} + +export default IndexPage; diff --git a/pages/register/index.tsx b/pages/register/index.tsx new file mode 100644 index 0000000..dca93d5 --- /dev/null +++ b/pages/register/index.tsx @@ -0,0 +1,229 @@ +import Link from 'next/link' +import Image from 'next/image' +import { Title } from '../../components/Title'; +import { useState } from 'react' +import { DatePicker } from 'antd'; +import Select from 'react-select'; +import Navbar from '@/components/NavBar'; +import Footer from '@/components/Footer'; + +const RegisterPage = () => { + const [email, setEmail] = useState('') + const [phone, setPhone] = useState('') + const [birthdate, setBirthdate] = useState(null); + const [gender, setGender] = useState(null); + const [name, setName] = useState('') + const [address, setAddress] = useState('') + const [city, setCity] = useState('') + const [password, setPassword] = useState('') + const [confirmPassword, setConfirmPassword] = useState('') + + const handleRegister = (e: React.FormEvent) => { + e.preventDefault() + } + + const genderOptions = [ + { value: 'male', label: 'Male' }, + { value: 'female', label: 'Female' }, + { value: 'other', label: 'Other' } + ]; + + return ( +
+ +
+ Register +
+
+ + + CGV Logo + + +
+
+
+
+ + setName(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+
+ + setEmail(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+
+ + setPhone(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+
+ + setBirthdate(dateString)} + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+
+ + setCity(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+
+
+ + setAddress(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+
+
+ + setPassword(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+
+ + setConfirmPassword(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
+
+ +
+

+ Already have an account?{' '} + + Login + +

+
+
+
+
+ ) +} + +export default RegisterPage diff --git a/pages/seat/[id].tsx b/pages/seat/[id].tsx new file mode 100644 index 0000000..878bf88 --- /dev/null +++ b/pages/seat/[id].tsx @@ -0,0 +1,85 @@ +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import Navbar from '@/components/NavBar'; +import SeatPicker from '@/components/SeatPicker'; +import LoadingSpinner from '@/components/LoadingSpinner'; +import MovieDetail from '@/components/MovieDetail'; +import moviesData, { Movie } from '@/functions/moviesData'; + +const SeatSelection: React.FC = () => { + const router = useRouter(); + const { id } = router.query; + const [loading, setLoading] = useState(true); + const [cinemaName, setCinemaName] = useState(null); + const [showtime, setShowtime] = useState(null); + const [movie, setMovie] = useState(null); + const [date, setDate] = useState(null); + const [selectedSeats, setSelectedSeats] = useState([]); + + useEffect(() => { + if (id) { + const [movieIdStr, cinema, time, dateString] = (id as string).split('-'); + const movieId = parseInt(decodeURIComponent(movieIdStr)); + setCinemaName(decodeURIComponent(cinema)); + setShowtime(decodeURIComponent(time)); + setDate(new Date(decodeURIComponent(dateString))); + + const selectedMovie = moviesData.find(m => m.id === movieId); + setMovie(selectedMovie || null); + setLoading(false); + } + }, [id]); + + const handleSeatSelection = (seats: string[]) => { + setSelectedSeats(seats); + }; + + const handleProceedToTransaction = () => { + if (!movie || !date || !cinemaName || !showtime || selectedSeats.length === 0) { + return; + } + + const transactionData = { + movieId: movie.id, + cinemaName, + showtime, + date: date.toISOString(), + selectedSeats, + }; + + router.push({ + pathname: `transaction/${id}`, + query: transactionData, + }); + }; + + if (loading) { + return ; + } + + if (!id || !cinemaName || !showtime || !movie || !date) { + return
Invalid URL parameters or movie not found.
; + } + + return ( +
+ +
+
+ +
+

Seat Selection Details

+

Cinema: {cinemaName}

+

Showtime: {showtime}

+

Date: {date.toDateString()}

+
+

Choose Your Seats

+ + +
+
+
+ ); +}; + +export default SeatSelection; diff --git a/pages/seat/transaction/[id].tsx b/pages/seat/transaction/[id].tsx new file mode 100644 index 0000000..f63a9e6 --- /dev/null +++ b/pages/seat/transaction/[id].tsx @@ -0,0 +1,64 @@ +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import Navbar from '@/components/NavBar'; +import LoadingSpinner from '@/components/LoadingSpinner'; + +const Transaction: React.FC = () => { + const router = useRouter(); + const { id } = router.query; + const [loading, setLoading] = useState(true); + const [transactionData, setTransactionData] = useState(null); + + useEffect(() => { + if (id) { + const fetchData = async () => { + try { + // Ubah id menjadi string + const transactionId = id.toString(); + + // Misalnya, Anda memiliki fungsi untuk mengambil data transaksi berdasarkan id + // Ganti fungsi ini dengan cara Anda mengambil data transaksi dari sumber data yang sesuai + const data = await fetchTransactionData(transactionId); + + // Set data transaksi dan hentikan loading + setTransactionData(data); + setLoading(false); + } catch (error) { + // Tangani kesalahan jika pengambilan data gagal + console.error('Error fetching transaction data:', error); + setLoading(false); + } + }; + + fetchData(); + } + }, [id]); + + if (loading) { + return ; + } + + if (!transactionData) { + return
Data transaksi tidak ditemukan.
; + } + + return ( +
+ +
+

Transaction Details

+
+

Transaction ID: {transactionData.id}

+

Movie ID: {transactionData.movieId}

+

Cinema: {transactionData.cinemaName}

+

Showtime: {transactionData.showtime}

+

Date: {transactionData.date}

+

Selected Seats: {transactionData.selectedSeats.join(', ')}

+

Total Price: {transactionData.totalPrice}

+
+
+
+ ); +}; + +export default Transaction; diff --git a/styles/globals.css b/styles/globals.css index 00ffe68..e49b47b 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -6,5 +6,5 @@ @import '@fortawesome/fontawesome-svg-core/styles.css'; #nprogress .bar { - background: limegreen !important; -} + background: red !important; +} \ No newline at end of file From 1ca4a0c1a4450f89179e389d464a90e13697f392 Mon Sep 17 00:00:00 2001 From: Nicholas Christopher Date: Sat, 18 May 2024 23:19:08 +0700 Subject: [PATCH 2/5] fix --- components/DayButtons.tsx | 2 +- components/SeatPicker.tsx | 3 +-- pages/movies/schedule/[id].tsx | 1 - pages/seat/[id].tsx | 9 ++++----- pages/seat/transaction/[id].tsx | 19 +++++++++---------- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/components/DayButtons.tsx b/components/DayButtons.tsx index 49081fa..44672e5 100644 --- a/components/DayButtons.tsx +++ b/components/DayButtons.tsx @@ -26,7 +26,7 @@ const DayButtons: React.FC = ({ onDateChange }) => { const handleButtonClick = (day: Date) => { setSelectedDay(day); - onDateChange(day); // Panggil fungsi onDateChange saat tanggal dipilih + onDateChange(day); }; return ( diff --git a/components/SeatPicker.tsx b/components/SeatPicker.tsx index 8e11790..0c6dbf3 100644 --- a/components/SeatPicker.tsx +++ b/components/SeatPicker.tsx @@ -17,10 +17,9 @@ const SeatPicker: React.FC = ({ rows, cols, onSeatSelect }) => } else { newSelectedSeats.add(seatId); } + onSeatSelect(Array.from(newSelectedSeats)); return newSelectedSeats; }); - - onSeatSelect(Array.from(selectedSeats)); }; const generateSeatLabel = (row: number, col: number) => { diff --git a/pages/movies/schedule/[id].tsx b/pages/movies/schedule/[id].tsx index addd5e4..903e36c 100644 --- a/pages/movies/schedule/[id].tsx +++ b/pages/movies/schedule/[id].tsx @@ -2,7 +2,6 @@ import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import Navbar from '@/components/NavBar'; import MovieDetail from '@/components/MovieDetail'; -import DayButtons from '@/components/DayButtons'; import MovieDate from '@/components/MovieDate'; import moviesData, { Movie } from '@/functions/moviesData'; import LoadingSpinner from '@/components/LoadingSpinner'; diff --git a/pages/seat/[id].tsx b/pages/seat/[id].tsx index 878bf88..62e1e56 100644 --- a/pages/seat/[id].tsx +++ b/pages/seat/[id].tsx @@ -23,7 +23,7 @@ const SeatSelection: React.FC = () => { setCinemaName(decodeURIComponent(cinema)); setShowtime(decodeURIComponent(time)); setDate(new Date(decodeURIComponent(dateString))); - + const selectedMovie = moviesData.find(m => m.id === movieId); setMovie(selectedMovie || null); setLoading(false); @@ -33,12 +33,11 @@ const SeatSelection: React.FC = () => { const handleSeatSelection = (seats: string[]) => { setSelectedSeats(seats); }; - const handleProceedToTransaction = () => { if (!movie || !date || !cinemaName || !showtime || selectedSeats.length === 0) { return; } - + const transactionData = { movieId: movie.id, cinemaName, @@ -46,9 +45,9 @@ const SeatSelection: React.FC = () => { date: date.toISOString(), selectedSeats, }; - + router.push({ - pathname: `transaction/${id}`, + pathname: `/seat/transaction/${id}`, query: transactionData, }); }; diff --git a/pages/seat/transaction/[id].tsx b/pages/seat/transaction/[id].tsx index f63a9e6..03b62cc 100644 --- a/pages/seat/transaction/[id].tsx +++ b/pages/seat/transaction/[id].tsx @@ -6,6 +6,7 @@ import LoadingSpinner from '@/components/LoadingSpinner'; const Transaction: React.FC = () => { const router = useRouter(); const { id } = router.query; + const { movieId, cinemaName, showtime, date, selectedSeats } = router.query; const [loading, setLoading] = useState(true); const [transactionData, setTransactionData] = useState(null); @@ -13,18 +14,17 @@ const Transaction: React.FC = () => { if (id) { const fetchData = async () => { try { - // Ubah id menjadi string - const transactionId = id.toString(); - - // Misalnya, Anda memiliki fungsi untuk mengambil data transaksi berdasarkan id - // Ganti fungsi ini dengan cara Anda mengambil data transaksi dari sumber data yang sesuai - const data = await fetchTransactionData(transactionId); - - // Set data transaksi dan hentikan loading + const data = { + id: id.toString(), + movieId: movieId.toString(), + cinemaName: cinemaName.toString(), + showtime: showtime.toString(), + date: new Date(date.toString()).toLocaleDateString(), + selectedSeats: selectedSeats.toString().split(','), + }; setTransactionData(data); setLoading(false); } catch (error) { - // Tangani kesalahan jika pengambilan data gagal console.error('Error fetching transaction data:', error); setLoading(false); } @@ -54,7 +54,6 @@ const Transaction: React.FC = () => {

Showtime: {transactionData.showtime}

Date: {transactionData.date}

Selected Seats: {transactionData.selectedSeats.join(', ')}

-

Total Price: {transactionData.totalPrice}

From 8c64176aadd8cb149e41ac26edbd66629a543b37 Mon Sep 17 00:00:00 2001 From: Nicholas Christopher Date: Sat, 18 May 2024 23:27:02 +0700 Subject: [PATCH 3/5] fix2 --- components/BannerPromotions.tsx | 106 -------------------------------- components/NavBar.tsx | 5 +- 2 files changed, 1 insertion(+), 110 deletions(-) diff --git a/components/BannerPromotions.tsx b/components/BannerPromotions.tsx index b29ef1c..277b2f4 100644 --- a/components/BannerPromotions.tsx +++ b/components/BannerPromotions.tsx @@ -1,109 +1,3 @@ -// import Link from 'next/link'; -// import React, { useState } from 'react'; - -// interface Promotion { -// id: number; -// title: string; -// description: string; -// imageUrl: string; -// } - -// const promotions: Promotion[] = [ -// { -// id: 1, -// title: 'Promo 1', -// description: 'Diskon hingga 50% untuk produk tertentu', -// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405171125514961.jpg', -// }, -// { -// id: 2, -// title: 'Promo 2', -// description: 'Gratis ongkir untuk pembelian di atas Rp100.000', -// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405100933495889.jpg', -// }, -// { -// id: 3, -// title: 'Promo 3', -// description: 'Beli 1 gratis 1 untuk produk tertentu', -// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', -// }, -// { -// id: 4, -// title: 'Promo 4', -// description: 'Diskon hingga 30% untuk produk tertentu', -// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', -// }, -// { -// id: 5, -// title: 'Promo 5', -// description: 'Beli 2 gratis 1 untuk produk tertentu', -// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', -// }, -// { -// id: 6, -// title: 'Promo 6', -// description: 'Cashback hingga 20% untuk produk tertentu', -// imageUrl: 'https://cdn.cgv.id/uploads_v2/promotions/2405/PR202405021526585021.jpg', -// }, -// ]; - -// const BannerPromotions: React.FC = () => { -// const [startIndex, setStartIndex] = useState(0); - -// const handleNext = () => { -// setStartIndex((prevIndex) => { -// const remainingItems = promotions.length - (prevIndex + 2); -// const newIndex = prevIndex + 1; -// return remainingItems >= 0 ? newIndex : prevIndex; -// }); -// }; - -// const handlePrev = () => { -// setStartIndex((prevIndex) => Math.max(prevIndex - 1, 0)); -// }; - -// return ( -//
-//
-//

PROMOTION

-// View All -//
-//
-//
-// {promotions.slice(startIndex, startIndex + 2 ).map((promotion) => ( -//
-//
-// {promotion.title} -//
-//

{promotion.title}

-//

{promotion.description}

-//
-//
-//
-// ))} -//
-// {startIndex > 0 && ( -// -// )} -// {startIndex < promotions.length - 2 && ( -// -// )} -//
-//
-// ); -// }; - -// export default BannerPromotions; - -// components/PromotionBanner.js import { useState } from 'react'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; import { createUseStyles } from 'react-jss'; diff --git a/components/NavBar.tsx b/components/NavBar.tsx index 5850ace..0d65f73 100644 --- a/components/NavBar.tsx +++ b/components/NavBar.tsx @@ -87,10 +87,7 @@ const Navbar: React.FC = () => { Movies
  • - Cinemas -
  • -
  • - Features + Promotions
  • From d973bd7a2a8765c923135dea3f0ba46a4251d43a Mon Sep 17 00:00:00 2001 From: Nicholas Christopher Date: Sat, 18 May 2024 23:32:22 +0700 Subject: [PATCH 4/5] Add Forgot Password Page --- pages/forgotpassword/index.tsx | 110 +++++++++++++++++++++++++++++++++ pages/login/index.tsx | 2 +- 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 pages/forgotpassword/index.tsx diff --git a/pages/forgotpassword/index.tsx b/pages/forgotpassword/index.tsx new file mode 100644 index 0000000..9c1f3ff --- /dev/null +++ b/pages/forgotpassword/index.tsx @@ -0,0 +1,110 @@ +import React, { useState } from 'react'; +import { useRouter } from 'next/router'; +import { signIn, useSession } from 'next-auth/react'; +import nProgress from 'nprogress'; +import Link from 'next/link'; +import Image from 'next/image'; +import { Title } from '../../components/Title'; +import Navbar from '@/components/NavBar'; +import Footer from '@/components/Footer'; + +const LoginPage = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const router = useRouter(); + const { data: session } = useSession(); + + if (session) { + router.push('/'); + return null; + } + + const handleLogin = async (e) => { + e.preventDefault(); + try { + const response = await fetch('/api/be/api/v1/Auth/Login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + email: email, + password: password, + phone: 0, + }) + }); + + if (response.ok) { + router.push('/'); + } else { + console.error('Login failed:', response.statusText); + } + } catch (error) { + console.error('Login failed:', error); + } + }; + + const handleOidcSignIn = () => { + nProgress.start(); + signIn('oidc'); + }; + + return ( +
    + +
    + Login +
    +
    + + + CGV Logo + + +
    +
    +
    + + setEmail(e.target.value)} + required + className="w-full px-3 py-2 mt-1 border rounded-md focus:outline-none focus:ring focus:ring-red-300 focus:border-red-500" + /> +
    + + + +
    +

    + Don't have an account yet?{' '} + + Register Now + +

    +
    +
    +
    +
    +
    +
    + ); +}; + +export default LoginPage; diff --git a/pages/login/index.tsx b/pages/login/index.tsx index 08e5c00..4924baa 100644 --- a/pages/login/index.tsx +++ b/pages/login/index.tsx @@ -112,7 +112,7 @@ const LoginPage = () => {

    From 5ffcb42e90e15aae348173fe7cdf2c0a3dcb61d0 Mon Sep 17 00:00:00 2001 From: Nicholas Christopher Date: Sat, 18 May 2024 23:46:39 +0700 Subject: [PATCH 5/5] Add History while login --- components/NavBar.tsx | 3 ++ pages/history/index.tsx | 86 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 pages/history/index.tsx diff --git a/components/NavBar.tsx b/components/NavBar.tsx index 0d65f73..17c4cb4 100644 --- a/components/NavBar.tsx +++ b/components/NavBar.tsx @@ -72,6 +72,9 @@ const Navbar: React.FC = () => {
  • +
  • + History +
  • )} diff --git a/pages/history/index.tsx b/pages/history/index.tsx new file mode 100644 index 0000000..3547f78 --- /dev/null +++ b/pages/history/index.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import Navbar from '@/components/NavBar'; +import movies from '@/functions/moviesData'; + +interface Transaction { + id: string; + ticketId: string; + cinema: string; + movieTitle: string; + date: string; + time: string; + price: number; + seats: string[]; +} + +const dummyTransactions: Transaction[] = [ + { + id: '1', + ticketId: 'TICKET001', + cinema: 'CGV Central Park', + movieTitle: 'Avengers: Endgame', + date: '2024-05-01', + time: '14:00', + price: 150000, + seats: ['A1', 'B2'] + }, + { + id: '2', + ticketId: 'TICKET002', + cinema: 'CGV Grand Indonesia', + movieTitle: 'Spider-Man: Far From Home', + date: '2024-05-02', + time: '15:30', + price: 200000, + seats: ['C3', 'D4'] + }, + { + id: '3', + ticketId: 'TICKET003', + cinema: 'CGV Plaza Senayan', + movieTitle: 'The Lion King', + date: '2024-05-03', + time: '12:00', + price: 100000, + seats: ['E5', 'F6'] + }, +]; + +const TransactionHistory: React.FC = () => { + return ( +
    + +
    +
    +

    Transaction History

    + {dummyTransactions.map((transaction) => { + const movie: Movie | undefined = movies.find((m) => m.title === transaction.movieTitle); + return ( +
    +
    +

    {transaction.movieTitle}

    +

    {transaction.cinema}

    +

    Date: {transaction.date}

    +

    Time: {transaction.time}

    +

    Ticket ID: {transaction.ticketId}

    +

    Seats: {transaction.seats.join(', ')}

    +
    +

    Rp. {transaction.price}

    +
    +
    + + {movie && ( +
    + {movie.title} +
    + )} +
    + ); + })} +
    +
    +
    + ); +}; + +export default TransactionHistory;