From 2118e10985169c3a95aee5b554adedf031367f2f Mon Sep 17 00:00:00 2001 From: SerhiiSemennikov Date: Sun, 15 Dec 2024 20:04:38 +0100 Subject: [PATCH 1/2] copy1 --- README.md | 2 +- src/App.tsx | 20 ++++- src/components/MovieCard/MovieCard.tsx | 4 +- src/components/NewMovie/NewMovie.tsx | 114 +++++++++++++++++++++++-- src/components/TextField/TextField.tsx | 12 ++- 5 files changed, 136 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 4a0f7851a..0f567ecd0 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,4 @@ const pattern = /^((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|( - Implement a solution following the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline). - Use the [React TypeScript cheat sheet](https://mate-academy.github.io/fe-program/js/extra/react-typescript). - Open one more terminal and run tests with `npm test` to ensure your solution is correct. -- Replace `` with your Github username in the [DEMO LINK](https://.github.io/react_movies-list-add-form/) and add it to the PR description. +- Replace `` with your Github username in the [DEMO LINK](https://SerhiiSemennikov.github.io/react_movies-list-add-form/) and add it to the PR description. diff --git a/src/App.tsx b/src/App.tsx index 34be670b0..1fdf1954e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,15 +2,31 @@ import './App.scss'; import { MoviesList } from './components/MoviesList'; import { NewMovie } from './components/NewMovie'; import moviesFromServer from './api/movies.json'; +import React, { useState, useCallback } from 'react'; +import { Movie } from './types/Movie'; export const App = () => { + const visibleMovies = [...moviesFromServer]; + const [movies, setMovies] = useState(visibleMovies); + + const addMovie = useCallback((newMovie: Movie) => { + setMovies(currentMovies => { + return [...currentMovies, newMovie]; + }); + }, []); + return (
- +
- {}} */ /> +
); diff --git a/src/components/MovieCard/MovieCard.tsx b/src/components/MovieCard/MovieCard.tsx index bfcebd1df..dfc23487b 100644 --- a/src/components/MovieCard/MovieCard.tsx +++ b/src/components/MovieCard/MovieCard.tsx @@ -24,7 +24,9 @@ export const MovieCard: React.FC = ({ movie }) => (

{movie.title}

- +
+

+
{movie.description}
diff --git a/src/components/NewMovie/NewMovie.tsx b/src/components/NewMovie/NewMovie.tsx index 85bace9dd..6edaa7320 100644 --- a/src/components/NewMovie/NewMovie.tsx +++ b/src/components/NewMovie/NewMovie.tsx @@ -1,30 +1,126 @@ import { useState } from 'react'; import { TextField } from '../TextField'; +import React from 'react'; +import { Movie } from '../../types/Movie'; -export const NewMovie = () => { +type Props = { + onAdd: (newMovie: Movie) => void; + //getMaxId: (movies: Movie[]) => number | (() => number); + movies: Movie[]; + errorMessage: string; + titleErrorMessage: string; +}; +function getMaxId(movies: Movie[]) { + return Math.max(...movies.map(m => movies.indexOf(m))); +} + +export const NewMovie: React.FC = ({ onAdd, movies }) => { // Increase the count after successful form submission // to reset touched status of all the `Field`s - const [count] = useState(0); + + const [count, setCount] = useState(getMaxId(movies) + 1); + + const [title, setTitle] = useState(''); + const [description, setDescription] = useState(''); + const [imgUrl, setImgUrl] = useState(''); + const [imdbUrl, setImdbUrl] = useState(''); + const [imdbId, setImdbId] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + const [titleErrorMessage, setTitleErrorMessage] = useState(''); + const pattern = + // eslint-disable-next-line max-len + /^((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@,.\w_]*)#?(?:[,.!/\\\w]*))?)$/; + const reset = () => { + setTitle(''); + setImgUrl(''); + setImdbUrl(''); + setImdbId(''); + setDescription(''); + setTitleErrorMessage(''); + setErrorMessage(''); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + const movie = { + title: title, + description: description, + imgUrl: imgUrl, + imdbUrl: imdbUrl, + imdbId: imdbId, + count: count, + }; + + //if (!title || !imdbUrl || !imgUrl) { + // return; + //} + if (!title) { + setTitleErrorMessage('should have some text'); + } else if (title.length < 5) { + setTitleErrorMessage('should have at least 5 chars'); + } + + if (!pattern.test(imgUrl) || !pattern.test(imdbUrl)) { + setErrorMessage('not valid'); + + return; + } + + if (!title || !imdbUrl || !imgUrl || title.length < 5) { + return; + } + + onAdd(movie); + setCount(currentCount => currentCount + 1); + reset(); + }; return ( -
+

Add a movie

{}} + value={title} + onChange={setTitle} required + errorMessage={titleErrorMessage} /> - + - + - + - +
diff --git a/src/components/TextField/TextField.tsx b/src/components/TextField/TextField.tsx index e24856c4b..ce1739085 100644 --- a/src/components/TextField/TextField.tsx +++ b/src/components/TextField/TextField.tsx @@ -7,6 +7,8 @@ type Props = { label?: string; placeholder?: string; required?: boolean; + errorMessage: string; + onChange?: (newValue: string) => void; }; @@ -18,6 +20,7 @@ export const TextField: React.FC = ({ name, value, label = name, + errorMessage, placeholder = `Enter ${label}`, required = false, onChange = () => {}, @@ -27,14 +30,14 @@ export const TextField: React.FC = ({ // To show errors only if the field was touched (onBlur) const [touched, setTouched] = useState(false); - const hasError = touched && required && !value; + + let hasError = touched && required && !value; return (
-
= ({ onBlur={() => setTouched(true)} />
- + {errorMessage && (hasError = false)} {hasError &&

{`${label} is required`}

} + {errorMessage && ( +

{`${label} ${errorMessage}`}

+ )}
); }; From f879e458a5602f101ed4c3e26c928318e12269ef Mon Sep 17 00:00:00 2001 From: SerhiiSemennikov Date: Mon, 16 Dec 2024 18:21:28 +0100 Subject: [PATCH 2/2] copy2 --- src/App.tsx | 3 +-- src/components/MovieCard/MovieCard.tsx | 4 +--- src/components/NewMovie/NewMovie.tsx | 16 ++++++++-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 1fdf1954e..7e4ccc4f7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,8 +6,7 @@ import React, { useState, useCallback } from 'react'; import { Movie } from './types/Movie'; export const App = () => { - const visibleMovies = [...moviesFromServer]; - const [movies, setMovies] = useState(visibleMovies); + const [movies, setMovies] = useState(moviesFromServer); const addMovie = useCallback((newMovie: Movie) => { setMovies(currentMovies => { diff --git a/src/components/MovieCard/MovieCard.tsx b/src/components/MovieCard/MovieCard.tsx index dfc23487b..bfcebd1df 100644 --- a/src/components/MovieCard/MovieCard.tsx +++ b/src/components/MovieCard/MovieCard.tsx @@ -24,9 +24,7 @@ export const MovieCard: React.FC = ({ movie }) => (

{movie.title}

-
-

-
+
{movie.description}
diff --git a/src/components/NewMovie/NewMovie.tsx b/src/components/NewMovie/NewMovie.tsx index 6edaa7320..7d7627ee6 100644 --- a/src/components/NewMovie/NewMovie.tsx +++ b/src/components/NewMovie/NewMovie.tsx @@ -5,19 +5,16 @@ import { Movie } from '../../types/Movie'; type Props = { onAdd: (newMovie: Movie) => void; - //getMaxId: (movies: Movie[]) => number | (() => number); + movies: Movie[]; errorMessage: string; titleErrorMessage: string; }; function getMaxId(movies: Movie[]) { - return Math.max(...movies.map(m => movies.indexOf(m))); + return Math.max(...movies.map(movie => movies.indexOf(movie))); } export const NewMovie: React.FC = ({ onAdd, movies }) => { - // Increase the count after successful form submission - // to reset touched status of all the `Field`s - const [count, setCount] = useState(getMaxId(movies) + 1); const [title, setTitle] = useState(''); @@ -51,9 +48,12 @@ export const NewMovie: React.FC = ({ onAdd, movies }) => { count: count, }; - //if (!title || !imdbUrl || !imgUrl) { - // return; - //} + if (movies.find(el => el.title === title)?.title) { + setTitleErrorMessage('this title already exist'); + + return; + } + if (!title) { setTitleErrorMessage('should have some text'); } else if (title.length < 5) {