diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..5dad715 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,7 @@ +FROM debian:12 + +WORKDIR /app + +RUN apt update &&\ + apt install git curl unzip -y &&\ + curl -fsSL https://bun.sh/install | bash \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..a13d85d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,18 @@ +{ + "workspaceFolder": "/workspace", + "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,Z", + "build": { + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": [ + "oven.bun-vscode", + "biomejs.biome", + "astro-build.astro-vscode" + ] + } + }, + "forwardPorts": [8080], + "runArgs": ["--network=dev-network", "--name=trepa-web-dev"] +} diff --git a/astro.config.mjs b/astro.config.mjs index 41abc79..aac78c4 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -10,4 +10,7 @@ export default defineConfig({ adapter: node({ mode: "standalone", }), + server: { + port: 8080 + } }); diff --git a/biome.json b/biome.json index 3867749..0b46c23 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "organizeImports": { "enabled": true }, diff --git a/bun.lockb b/bun.lockb index df4263a..2f4f837 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 0f43654..fb2cb2c 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,19 @@ "astro": "astro" }, "dependencies": { - "@astrojs/check": "^0.8.2", - "@astrojs/node": "^8.3.2", - "@astrojs/react": "^3.6.0", - "@types/react": "^18.3.3", + "@astrojs/check": "^0.9.4", + "@astrojs/node": "^9.1.3", + "@astrojs/react": "^4.2.2", + "@types/react": "^18.3.5", "@types/react-dom": "^18.3.0", - "astro": "^4.12.2", + "astro": "^5.5.5", "bootstrap": "^5.3.3", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-icons": "^5.2.1", - "typescript": "^5.5.4" + "react-icons": "^5.5.0", + "typescript": "^5.8.2" }, "devDependencies": { - "@biomejs/biome": "1.8.3" + "@biomejs/biome": "1.9.4" } } diff --git a/src/components/DebouceInput.tsx b/src/components/DebouceInput.tsx deleted file mode 100644 index 57f6396..0000000 --- a/src/components/DebouceInput.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import type React from "react"; -import { useEffect, useRef, useState } from "react"; - -interface DebounceProps { - debouceDelay: number; - onDebounce: (value: string) => void; -} - -type DebouceInputProps = DebounceProps & - React.InputHTMLAttributes; - -export default function DebounceInput(props: DebouceInputProps) { - const [value, setValue] = useState(""); - - const debounceTimeout = useRef(null); - - useEffect(() => { - if (typeof props.value === "string") { - setValue(props.value); - } else { - setValue(""); - } - }, [props.value]); - - function handleOnChange(e: React.ChangeEvent) { - if (debounceTimeout.current) { - clearTimeout(debounceTimeout.current); - } - - setValue(e.target.value); - - debounceTimeout.current = setTimeout( - () => props.onDebounce(e.target.value), - props.debouceDelay, - ); - - if (props.onChange) { - props.onChange(e); - } - } - - const inputAttr = { - ...props, - debouceDelay: undefined, - onDebounce: undefined, - }; - - return ( - - ); -} diff --git a/src/components/MoviesSearchInput.tsx b/src/components/MoviesSearchInput.tsx deleted file mode 100644 index bd29c10..0000000 --- a/src/components/MoviesSearchInput.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { useState } from "react"; -import DebounceInput from "./DebouceInput"; - -interface MoviesSearchInput { - searchUrl: string; -} - -export default function MoviesSearchInput({ searchUrl }: MoviesSearchInput) { - const [value, setValue] = useState(""); - - function handleOnDebounce(value: string) {} - - function handleOnChange(event: React.ChangeEvent) { - setValue(event.target.value); - } - - function handleOnKeyDown(e: React.KeyboardEvent) { - if (e.key === "Enter") { - window.location.href = `${searchUrl}?query=${value}`; - } - } - - return ( -
- - -
- ); -} diff --git a/src/components/TmdbSearch.tsx b/src/components/TmdbSearch.tsx deleted file mode 100644 index 1771ac7..0000000 --- a/src/components/TmdbSearch.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useState } from "react"; -import type { Movie } from "../types"; -import TmdbSearchInput from "./TmdbSearchInput"; -import GroupMoviesList from "./lists/movies/GroupMoviesList"; - -interface TmdbSearchProps { - onSearch: (movie: Movie) => void; -} - -export default function TmdbSearch({ onSearch }: TmdbSearchProps) { - const [movies, setMovies] = useState([]); - - function handleOnSearch(movies: Movie[]) { - setMovies(movies); - } - - return ( - <> -
-
- -
-
-
-
- -
-
- - ); -} diff --git a/src/components/TmdbSearchInput.tsx b/src/components/TmdbSearchInput.tsx deleted file mode 100644 index d2c0191..0000000 --- a/src/components/TmdbSearchInput.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Movie } from "../types"; -import DebounceInput from "./DebouceInput"; - -interface TmdbSearchInputProps { - onSearch: (movies: Movie[]) => void; -} - -export default function TmdbSearchInput({ onSearch }: TmdbSearchInputProps) { - function handleOnSearchInputDebounce(value: string) { - fetch(`/api/tmdb/movies/search?query=${value}`, { - method: "GET", - credentials: "include", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - }) - .then((res) => res.json()) - .then((json) => { - onSearch(json.movies); - }) - .catch((err) => console.log(err)); - onSearch([]); - } - - return ( - - ); -} diff --git a/src/components/forms/AddMovieForm.tsx b/src/components/forms/AddMovieForm.tsx deleted file mode 100644 index eb05a90..0000000 --- a/src/components/forms/AddMovieForm.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useState } from "react"; -import TmdbSearch from "../TmdbSearch"; -import type { CreateMovie, Movie } from "../../types"; -import MovieFormData from "./MovieFormData"; - -export default function AddMovieForm() { - const [tmdbMovie, setTmdbMovie] = useState(null); - - function handleOnSearch(movie: Movie) { - setTmdbMovie(movie); - } - - const createMovie: CreateMovie | undefined = tmdbMovie - ? { - title: tmdbMovie.title, - backdrop_path: tmdbMovie.backdrop_path, - poster_path: tmdbMovie.poster_path, - overview: tmdbMovie.overview, - release_date: tmdbMovie.release_date, - tmdb_id: tmdbMovie.id, - } - : undefined; - - return ( -
-
- -
-
- -
-
- ); -} diff --git a/src/components/forms/LoginForm.tsx b/src/components/forms/LoginForm.tsx deleted file mode 100644 index 9c700a9..0000000 --- a/src/components/forms/LoginForm.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useState } from "react"; - -export interface LoginFormValue { - username: string; - password: string; -} - -interface LoginFormProps { - onSubmit: (data: LoginFormValue) => void; - disabled?: boolean; -} - -export default function LoginForm(props: LoginFormProps) { - const [form, setForm] = useState({ - username: "", - password: "", - }); - - function handleInputChange(event: React.ChangeEvent) { - const target = event.target; - const name = target.name; - const value = target.value; - - setForm({ - ...form, - [name]: value, - }); - } - - function handleBtnClick() { - props.onSubmit({ ...form }); - } - - return ( -
-
-
-
- - -
-
- - -
-
-
- -
-
-
- ); -} diff --git a/src/components/forms/LoginFormData.tsx b/src/components/forms/LoginFormData.tsx deleted file mode 100644 index 443c019..0000000 --- a/src/components/forms/LoginFormData.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { useState } from "react"; -import LoginForm, { type LoginFormValue } from "./LoginForm"; - -export default function LoginFormData() { - const [loading, setLoading] = useState(false); - - function handleOnFormSubmit(data: LoginFormValue) { - const searchParams = new URLSearchParams(window.location.search); - setLoading(true); - - fetch("/api/accounts/login", { - method: "POST", - body: JSON.stringify(data), - credentials: "include", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - }) - .then((res) => res.json()) - .then(() => { - window.location.href = searchParams.get("redirect") || "/home"; - }) - .catch((err) => console.log(err)) - .finally(() => setLoading(false)); - } - - return ; -} diff --git a/src/components/forms/MovieForm.tsx b/src/components/forms/MovieForm.tsx deleted file mode 100644 index 0b1d38d..0000000 --- a/src/components/forms/MovieForm.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { useEffect, useState } from "react"; -import type { CreateMovie } from "../../types"; - -interface MovieFormProps { - defaultValue?: CreateMovie; - disabled?: boolean; - onSubmit: (createMovie: CreateMovie) => void; -} - -export default function MovieForm({ - defaultValue, - onSubmit, - disabled, -}: MovieFormProps) { - const [form, setForm] = useState( - defaultValue || { - title: "", - overview: "", - release_date: "", - backdrop_path: "", - poster_path: "", - tmdb_id: 0, - }, - ); - - useEffect(() => { - if (defaultValue) setForm(defaultValue); - }, [defaultValue]); - - function handleInputChange( - event: React.ChangeEvent, - ) { - const target = event.target; - const name = target.name; - const value = target.value; - - setForm({ - ...form, - [name]: value, - }); - } - - function handleBtnClick() { - onSubmit({ ...form }); - } - - return ( -
-
-
-
- - -
-
-