a lot of stuff...
This commit is contained in:
parent
9a9f66b5b9
commit
21547959c9
16 changed files with 291 additions and 32 deletions
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
11
.vscode/launch.json
vendored
11
.vscode/launch.json
vendored
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
13
.vscode/settings.json
vendored
13
.vscode/settings.json
vendored
|
@ -1,8 +1,7 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"[astro]": {
|
||||
"editor.defaultFormatter": "astro-build.astro-vscode"
|
||||
},
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"[astro]": {
|
||||
"editor.defaultFormatter": "astro-build.astro-vscode"
|
||||
}
|
||||
}
|
||||
|
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -9,7 +9,7 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.8.0",
|
||||
"@astrojs/check": "^0.8.1",
|
||||
"@astrojs/node": "^8.3.2",
|
||||
"@astrojs/react": "^3.6.0",
|
||||
"@types/react": "^18.3.3",
|
||||
|
|
|
@ -33,6 +33,10 @@ export default function DebounceInput(props: DebouceInputProps) {
|
|||
() => props.onDebounce(e.target.value),
|
||||
props.debouceDelay,
|
||||
);
|
||||
|
||||
if (props.onChange) {
|
||||
props.onChange(e);
|
||||
}
|
||||
}
|
||||
|
||||
const inputAttr = {
|
||||
|
@ -45,9 +49,11 @@ export default function DebounceInput(props: DebouceInputProps) {
|
|||
<input
|
||||
className={props.className}
|
||||
placeholder={props.placeholder}
|
||||
id={props.id}
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={handleOnChange}
|
||||
onKeyDown={props.onKeyDown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
37
src/components/MoviesSearchInput.tsx
Normal file
37
src/components/MoviesSearchInput.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { useState } from "react";
|
||||
import DebounceInput from "./DebouceInput";
|
||||
|
||||
interface MoviesSearchInput {
|
||||
searchUrl: string;
|
||||
}
|
||||
|
||||
export default function MoviesSearchInput({ searchUrl }: MoviesSearchInput) {
|
||||
const [value, setValue] = useState<string>("");
|
||||
|
||||
function handleOnDebounce(value: string) {}
|
||||
|
||||
function handleOnChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
setValue(event.target.value);
|
||||
}
|
||||
|
||||
function handleOnKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
if (e.key === "Enter") {
|
||||
window.location.href = `${searchUrl}?q=${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="form-floating mb-3">
|
||||
<DebounceInput
|
||||
debouceDelay={1000}
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="moviesearch"
|
||||
onChange={handleOnChange}
|
||||
onDebounce={handleOnDebounce}
|
||||
onKeyDown={handleOnKeyDown}
|
||||
/>
|
||||
<label className="form-label">Search a movie</label>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -2,12 +2,15 @@ import { useState } from "react";
|
|||
import type { CreateMovie } from "../../types";
|
||||
import MovieForm from "./MovieForm";
|
||||
|
||||
declare let bootstrap: any;
|
||||
|
||||
interface MovieFormDataProps {
|
||||
defaultValue?: CreateMovie;
|
||||
}
|
||||
|
||||
export default function MovieFormData({ defaultValue }: MovieFormDataProps) {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [showModal, setShowModal] = useState<boolean>(false);
|
||||
|
||||
function handleOnSubmit(createMovie: CreateMovie) {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
@ -24,6 +27,11 @@ export default function MovieFormData({ defaultValue }: MovieFormDataProps) {
|
|||
})
|
||||
.then((res) => res.json())
|
||||
.then((movie) => {
|
||||
const myModal = new bootstrap.Modal(
|
||||
document.getElementById("exampleModal"),
|
||||
);
|
||||
|
||||
myModal?.show();
|
||||
console.log(movie);
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
|
@ -31,10 +39,44 @@ export default function MovieFormData({ defaultValue }: MovieFormDataProps) {
|
|||
}
|
||||
|
||||
return (
|
||||
<MovieForm
|
||||
defaultValue={defaultValue}
|
||||
disabled={loading}
|
||||
onSubmit={handleOnSubmit}
|
||||
/>
|
||||
<>
|
||||
<MovieForm
|
||||
defaultValue={defaultValue}
|
||||
disabled={loading}
|
||||
onSubmit={handleOnSubmit}
|
||||
/>
|
||||
<div className="modal fade" id="exampleModal" role="dialog">
|
||||
<div className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Modal title</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>Modal body text goes here.</p>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-primary">
|
||||
Save changes
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
data-dismiss="modal"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
18
src/components/lists/movies/CardMoviesList.tsx
Normal file
18
src/components/lists/movies/CardMoviesList.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import type { Movie } from "../../../types";
|
||||
import CardMoviesListItem from "./CardMoviesListItem";
|
||||
|
||||
interface CardMoviesListProps {
|
||||
movies: Movie[];
|
||||
}
|
||||
|
||||
export default function CardMoviesList({ movies }: CardMoviesListProps) {
|
||||
const items = movies.map((elt) => {
|
||||
return (
|
||||
<div key={elt.id} className="col-2 mb-4">
|
||||
<CardMoviesListItem movie={elt} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return <div className="row">{items}</div>;
|
||||
}
|
50
src/components/lists/movies/CardMoviesListItem.tsx
Normal file
50
src/components/lists/movies/CardMoviesListItem.tsx
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { FaEye } from "react-icons/fa";
|
||||
import type { Movie } from "../../../types";
|
||||
|
||||
interface CardMoviesListItemProps {
|
||||
movie: Movie;
|
||||
}
|
||||
|
||||
export default function CardMoviesListItem({ movie }: CardMoviesListItemProps) {
|
||||
return (
|
||||
<div
|
||||
className="cardshadow"
|
||||
style={{
|
||||
backgroundImage: `url(${movie.poster_path})`,
|
||||
backgroundPosition: "center",
|
||||
backgroundSize: "cover",
|
||||
height: "320px",
|
||||
borderRadius: "5px",
|
||||
}}
|
||||
>
|
||||
<div style={{ height: "100%", width: "100%" }} className="movieitem">
|
||||
<div style={{ height: "100%" }} className="row justify-content-center">
|
||||
<div style={{ height: "100%" }} className="col align-self-center">
|
||||
<div style={{ height: "50%" }} className="row">
|
||||
<div className="col align-self-end">
|
||||
<h5 style={{ textAlign: "center", verticalAlign: "center" }}>
|
||||
{movie.title}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{ height: "50%" }}
|
||||
className="row justify-content-center"
|
||||
>
|
||||
<div className="col-8 align-self-center">
|
||||
<div className="d-grid">
|
||||
<a
|
||||
href={`/home/movies/${movie.id}`}
|
||||
className="btn btn-primary"
|
||||
>
|
||||
<FaEye />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -6,6 +6,7 @@ interface Props {
|
|||
const { title } = Astro.props;
|
||||
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "../styles/index.css";
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
|
@ -19,7 +20,7 @@ import "bootstrap/dist/css/bootstrap.min.css";
|
|||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="container">
|
||||
<slot />
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1,9 +1,43 @@
|
|||
---
|
||||
import MoviesSearchInput from "../../components/MoviesSearchInput";
|
||||
import CardMoviesList from "../../components/lists/movies/CardMoviesList";
|
||||
import HomeLayout from "../../layouts/HomeLayout.astro";
|
||||
import type { Movie } from "../../types";
|
||||
|
||||
const jwt = Astro.cookies.get("jwt")?.value as string;
|
||||
const url = new URL(Astro.request.url);
|
||||
|
||||
const queryParams = new URLSearchParams();
|
||||
const query = url.searchParams.get("q");
|
||||
|
||||
if (query) {
|
||||
queryParams.append("q", query);
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${import.meta.env.API_URL}/movies?${queryParams.toString()}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const resBody = (await res.json()) as { movies: Movie[] };
|
||||
const account = Astro.locals.account;
|
||||
---
|
||||
|
||||
<HomeLayout title="Movies">
|
||||
Hello {account?.username}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<MoviesSearchInput searchUrl="/home" client:load />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<CardMoviesList movies={resBody?.movies || []} />
|
||||
</div>
|
||||
</div>
|
||||
</HomeLayout>
|
||||
|
|
61
src/pages/home/movies/[id].astro
Normal file
61
src/pages/home/movies/[id].astro
Normal file
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
import { FaEye } from "react-icons/fa";
|
||||
import HomeLayout from "../../../layouts/HomeLayout.astro";
|
||||
import type { Movie } from "../../../types";
|
||||
|
||||
const jwt = Astro.cookies.get("jwt")?.value as string;
|
||||
const { id } = Astro.params;
|
||||
|
||||
const res = await fetch(`${import.meta.env.API_URL}/movies/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
});
|
||||
|
||||
const movie = (await res.json()).movie as Movie;
|
||||
---
|
||||
|
||||
<HomeLayout title={movie.title}>
|
||||
<>
|
||||
<h1>{movie.title}</h1>
|
||||
<div class="card">
|
||||
<div
|
||||
class="card-body"
|
||||
style={{
|
||||
background: `linear-gradient(to left, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 1), rgba(0, 0, 0, 1)), url(${movie.backdrop_path})`,
|
||||
backgroundPosition: "center",
|
||||
backgroundSize: "cover",
|
||||
minHeight: "512px",
|
||||
}}
|
||||
>
|
||||
<div class="row" style={{ height: "512px" }}>
|
||||
<div class="col align-self-center">
|
||||
<p>{movie.overview}</p>
|
||||
</div>
|
||||
<div class="col align-self-center">
|
||||
<div class="d-grid">
|
||||
<a
|
||||
href={`/home/movies/${movie.id}`}
|
||||
class="btn btn-primary"
|
||||
>
|
||||
Watch
|
||||
</a>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<div class="d-grid">
|
||||
<a
|
||||
href={`/home/movies/${movie.id}`}
|
||||
class="btn btn-secondary"
|
||||
>
|
||||
Share
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</HomeLayout>
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
import AddMovieForm from "../../../components/forms/AddMovieForm";
|
||||
import TmdbSearch from "../../../components/TmdbSearch";
|
||||
import HomeLayout from "../../../layouts/HomeLayout.astro";
|
||||
---
|
||||
|
||||
|
|
|
@ -4,5 +4,5 @@ import EmptyLayout from "../layouts/EmptyLayout.astro";
|
|||
---
|
||||
|
||||
<EmptyLayout title="Trepa - Connexion">
|
||||
<LoginFormData client:load />
|
||||
<LoginFormData client:load />
|
||||
</EmptyLayout>
|
||||
|
|
27
src/styles/index.css
Normal file
27
src/styles/index.css
Normal file
|
@ -0,0 +1,27 @@
|
|||
.cardshadow {
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
|
||||
transition: box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.cardshadow:hover {
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.movieitem {
|
||||
opacity: 0;
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.movieitem:hover {
|
||||
animation: 0.2s ease-in-out forwards moviehover;
|
||||
}
|
||||
|
||||
@keyframes moviehover {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue