a lot of stuff...

This commit is contained in:
qpismont 2024-07-16 22:41:10 +02:00
parent 9a9f66b5b9
commit 21547959c9
16 changed files with 291 additions and 32 deletions

View file

@ -1,4 +0,0 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored
View file

@ -1,11 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

11
.vscode/settings.json vendored
View file

@ -1,8 +1,7 @@
{ {
"editor.formatOnSave": true, "editor.formatOnSave": true,
"[astro]": { "editor.defaultFormatter": "biomejs.biome",
"editor.defaultFormatter": "astro-build.astro-vscode" "[astro]": {
}, "editor.defaultFormatter": "astro-build.astro-vscode"
"editor.defaultFormatter": "biomejs.biome", }
"typescript.tsdk": "node_modules/typescript/lib"
} }

BIN
bun.lockb

Binary file not shown.

View file

@ -9,7 +9,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/check": "^0.8.0", "@astrojs/check": "^0.8.1",
"@astrojs/node": "^8.3.2", "@astrojs/node": "^8.3.2",
"@astrojs/react": "^3.6.0", "@astrojs/react": "^3.6.0",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",

View file

@ -33,6 +33,10 @@ export default function DebounceInput(props: DebouceInputProps) {
() => props.onDebounce(e.target.value), () => props.onDebounce(e.target.value),
props.debouceDelay, props.debouceDelay,
); );
if (props.onChange) {
props.onChange(e);
}
} }
const inputAttr = { const inputAttr = {
@ -45,9 +49,11 @@ export default function DebounceInput(props: DebouceInputProps) {
<input <input
className={props.className} className={props.className}
placeholder={props.placeholder} placeholder={props.placeholder}
id={props.id}
type="text" type="text"
value={value} value={value}
onChange={handleOnChange} onChange={handleOnChange}
onKeyDown={props.onKeyDown}
/> />
); );
} }

View 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>
);
}

View file

@ -2,12 +2,15 @@ import { useState } from "react";
import type { CreateMovie } from "../../types"; import type { CreateMovie } from "../../types";
import MovieForm from "./MovieForm"; import MovieForm from "./MovieForm";
declare let bootstrap: any;
interface MovieFormDataProps { interface MovieFormDataProps {
defaultValue?: CreateMovie; defaultValue?: CreateMovie;
} }
export default function MovieFormData({ defaultValue }: MovieFormDataProps) { export default function MovieFormData({ defaultValue }: MovieFormDataProps) {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [showModal, setShowModal] = useState<boolean>(false);
function handleOnSubmit(createMovie: CreateMovie) { function handleOnSubmit(createMovie: CreateMovie) {
const searchParams = new URLSearchParams(window.location.search); const searchParams = new URLSearchParams(window.location.search);
@ -24,6 +27,11 @@ export default function MovieFormData({ defaultValue }: MovieFormDataProps) {
}) })
.then((res) => res.json()) .then((res) => res.json())
.then((movie) => { .then((movie) => {
const myModal = new bootstrap.Modal(
document.getElementById("exampleModal"),
);
myModal?.show();
console.log(movie); console.log(movie);
}) })
.catch((err) => console.log(err)) .catch((err) => console.log(err))
@ -31,10 +39,44 @@ export default function MovieFormData({ defaultValue }: MovieFormDataProps) {
} }
return ( return (
<MovieForm <>
defaultValue={defaultValue} <MovieForm
disabled={loading} defaultValue={defaultValue}
onSubmit={handleOnSubmit} 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">&times;</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>
</>
); );
} }

View 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>;
}

View 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>
);
}

View file

@ -6,6 +6,7 @@ interface Props {
const { title } = Astro.props; const { title } = Astro.props;
import "bootstrap/dist/css/bootstrap.min.css"; import "bootstrap/dist/css/bootstrap.min.css";
import "../styles/index.css";
--- ---
<!doctype html> <!doctype html>
@ -19,7 +20,7 @@ import "bootstrap/dist/css/bootstrap.min.css";
<title>{title}</title> <title>{title}</title>
</head> </head>
<body> <body>
<div class="container-fluid"> <div class="container">
<slot /> <slot />
</div> </div>
</body> </body>

View file

@ -1,9 +1,43 @@
--- ---
import MoviesSearchInput from "../../components/MoviesSearchInput";
import CardMoviesList from "../../components/lists/movies/CardMoviesList";
import HomeLayout from "../../layouts/HomeLayout.astro"; 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; const account = Astro.locals.account;
--- ---
<HomeLayout title="Movies"> <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> </HomeLayout>

View 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>

View file

@ -1,6 +1,5 @@
--- ---
import AddMovieForm from "../../../components/forms/AddMovieForm"; import AddMovieForm from "../../../components/forms/AddMovieForm";
import TmdbSearch from "../../../components/TmdbSearch";
import HomeLayout from "../../../layouts/HomeLayout.astro"; import HomeLayout from "../../../layouts/HomeLayout.astro";
--- ---

View file

@ -4,5 +4,5 @@ import EmptyLayout from "../layouts/EmptyLayout.astro";
--- ---
<EmptyLayout title="Trepa - Connexion"> <EmptyLayout title="Trepa - Connexion">
<LoginFormData client:load /> <LoginFormData client:load />
</EmptyLayout> </EmptyLayout>

27
src/styles/index.css Normal file
View 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;
}
}