diff --git a/src/meta/play-meta.js b/src/meta/play-meta.js index 5edc878dec..6deb43a0a3 100644 --- a/src/meta/play-meta.js +++ b/src/meta/play-meta.js @@ -10,13 +10,14 @@ import { RandomMemeGenerator, Keeper, ReactTodoApp, -QuoteGenerator, + QuoteGenerator, ExpandingCards, AnalogClock, PasswordGenerator, -WhyTypescript, -NetlifyCardGame, -//import play here + WhyTypescript, + NetlifyCardGame, + RegistrationForm, + //import play here } from "plays"; export const plays = [ @@ -86,89 +87,118 @@ export const plays = [ tags: "Recursion, Tree", github: "green-roots", featured: false, - }, { - id: 'pl-counter', - name: 'Counter', - description: 'A simple counter which increments the value upto a certain limit!', - component: () => {return }, - path: '/plays/counter', - level: 'Beginner', - tags: 'JSX, State, Props', - github: 'murtuzaalisurti', - featured: false - }, { - id: 'pl-states', - name: 'States', - description: 'States in Functional Components', - component: () => {return }, - path: '/plays/states', - level: 'Beginner', - tags: 'Hooks,State,JSX', - github: 'Abhishek-90', - cover: '', - blog: 'https://abhishek-90.github.io/My-Portfolio/' - }, { - id: 'pl-social-card', - name: 'Social Card', - description: 'The Social Card helps you telling who you are using photo, name, and other social footprints.', - component: () => {return }, - path: '/plays/social-card', - level: 'Intermediate', - tags: 'Form,Events,Complex State', - github: 'atapas', - cover: '', - blog: 'https://blog.greenroots.info/how-to-create-react-form-with-a-single-change-event-handler', - video: '' - }, { - id: 'pl-random-meme-generator', - name: 'Random Meme Generator', - description: 'A project to demonstrate the use of API to fetch random memes! It also demonstrates how you can do event handling!', - component: () => {return }, - path: '/plays/random-meme-generator', - level: 'Beginner', - tags: 'JSX,Hooks,API,EventHandling', - github: 'murtuzaalisurti', - cover: '', - blog: '', - video: '' - }, { - id: 'pl-react-todo-app', - name: 'React Todo App', - description: 'It is a simple Todo App which keeps track of your regular work', - component: () => { return }, - path: '/plays/react-todo-app', - level: 'Beginner', - tags: 'ReactHooks, JavaScript, Css, React State', - github: 'nirban256', - cover: 'https://res.cloudinary.com/atapas/image/upload/v1650866465/demos/cover_y20bzk.png', - blog: '', - video: '' - },{ - id: 'pl-quote-generator', - name: 'Random Quote Generator', - description: 'Randomly Generate quotes from 3rd Party API', - component: () => {return }, - path: '/plays/quote-generator', - level: 'Intermediate', - tags: 'Hooks,API,Async/Await', - github: 'Abhishek-90', - cover: 'https://i0.wp.com/dariusforoux.com/wp-content/uploads/2015/08/motivational-quotes.png?fit=2048%2C1536&ssl=1', - blog: '', - video: '' - }, - { - id: 'pl-keeper', - name: 'Keeper', - description: 'Keeper is the clone of google keep where we can save and delete our notes', - component: () => {return }, - path: '/plays/keeper', - level: 'Intermediate', - tags: 'JSX,Hooks,ReactDOM', - github: 'Shivam-Katare', - cover: 'https://res.cloudinary.com/dbjmy6wdu/image/upload/v1651678725/keepicon_jsn5bh.png', - blog: '', - video: '' - }, { + }, + { + id: "pl-counter", + name: "Counter", + description: + "A simple counter which increments the value upto a certain limit!", + component: () => { + return ; + }, + path: "/plays/counter", + level: "Beginner", + tags: "JSX, State, Props", + github: "murtuzaalisurti", + featured: false, + }, + { + id: "pl-states", + name: "States", + description: "States in Functional Components", + component: () => { + return ; + }, + path: "/plays/states", + level: "Beginner", + tags: "Hooks,State,JSX", + github: "Abhishek-90", + cover: "", + blog: "https://abhishek-90.github.io/My-Portfolio/", + }, + { + id: "pl-social-card", + name: "Social Card", + description: + "The Social Card helps you telling who you are using photo, name, and other social footprints.", + component: () => { + return ; + }, + path: "/plays/social-card", + level: "Intermediate", + tags: "Form,Events,Complex State", + github: "atapas", + cover: "", + blog: "https://blog.greenroots.info/how-to-create-react-form-with-a-single-change-event-handler", + video: "", + }, + { + id: "pl-random-meme-generator", + name: "Random Meme Generator", + description: + "A project to demonstrate the use of API to fetch random memes! It also demonstrates how you can do event handling!", + component: () => { + return ; + }, + path: "/plays/random-meme-generator", + level: "Beginner", + tags: "JSX,Hooks,API,EventHandling", + github: "murtuzaalisurti", + cover: "", + blog: "", + video: "", + }, + { + id: "pl-react-todo-app", + name: "React Todo App", + description: + "It is a simple Todo App which keeps track of your regular work", + component: () => { + return ; + }, + path: "/plays/react-todo-app", + level: "Beginner", + tags: "ReactHooks, JavaScript, Css, React State", + github: "nirban256", + cover: + "https://res.cloudinary.com/atapas/image/upload/v1650866465/demos/cover_y20bzk.png", + blog: "", + video: "", + }, + { + id: "pl-quote-generator", + name: "Random Quote Generator", + description: "Randomly Generate quotes from 3rd Party API", + component: () => { + return ; + }, + path: "/plays/quote-generator", + level: "Intermediate", + tags: "Hooks,API,Async/Await", + github: "Abhishek-90", + cover: + "https://i0.wp.com/dariusforoux.com/wp-content/uploads/2015/08/motivational-quotes.png?fit=2048%2C1536&ssl=1", + blog: "", + video: "", + }, + { + id: "pl-keeper", + name: "Keeper", + description: + "Keeper is the clone of google keep where we can save and delete our notes", + component: () => { + return ; + }, + path: "/plays/keeper", + level: "Intermediate", + tags: "JSX,Hooks,ReactDOM", + github: "Shivam-Katare", + cover: + "https://res.cloudinary.com/dbjmy6wdu/image/upload/v1651678725/keepicon_jsn5bh.png", + blog: "", + video: "", + }, + { id: "pl-expanding-cards", name: "Expanding-Cards", description: @@ -184,7 +214,8 @@ export const plays = [ cover: "", blog: "", video: "", - }, { + }, + { id: "pl-analog-clock", name: "Analog-Clock", description: "A beautiful wall clock", @@ -198,47 +229,77 @@ export const plays = [ cover: "", blog: "", video: "", - featured: true, - }, { - id: 'pl-password-generator', - name: 'Password Generator', - description: 'Its a simple password generator built in react using what user can generate password and customize their requirements in choosing characters and number while generating a medium or strong level password.', - component: () => {return }, - path: '/plays/password-generator', - level: 'Beginner', - tags: 'PasswordGenerator', - github: 'Angryman18', - cover: 'https://securityintelligence.com/wp-content/uploads/2018/10/si-eight-character-password-feature.jpg', - blog: '', - video: '', featured: true, - }, { - id: 'pl-why-typescript', - name: 'Why Typescript', - description: 'A simplistic way of understanding the existence of TypeScript', - component: () => {return }, - path: '/plays/why-typescript', - level: 'Intermediate', - tags: 'TSX,TypeScript,Learning,KnowWhat', - github: 'koustov', - cover: 'https://res.cloudinary.com/dgtdljyul/image/upload/v1651923177/ts_why_adazpf.png', - blog: '', - video: '', - language: 'ts', + }, + { + id: "pl-password-generator", + name: "Password Generator", + description: + "Its a simple password generator built in react using what user can generate password and customize their requirements in choosing characters and number while generating a medium or strong level password.", + component: () => { + return ; + }, + path: "/plays/password-generator", + level: "Beginner", + tags: "PasswordGenerator", + github: "Angryman18", + cover: + "https://securityintelligence.com/wp-content/uploads/2018/10/si-eight-character-password-feature.jpg", + blog: "", + video: "", featured: true, - }, { - id: 'pl-memory-game', - name: 'Memory Game', - description: 'simple memory game or memory testing game build with ReactJS', - component: () => {return }, - path: '/plays/memory-game', - level: 'Advanced', - tags: 'MemoryGame, CardGame, ReactJS', - github: 'Angryman18', - cover: 'https://cdn.pixabay.com/photo/2017/01/03/16/42/klee-1949946_960_720.jpg', - blog: '', - video: '', - language: 'js', + }, + { + id: "pl-why-typescript", + name: "Why Typescript", + description: + "A simplistic way of understanding the existence of TypeScript", + component: () => { + return ; + }, + path: "/plays/why-typescript", + level: "Intermediate", + tags: "TSX,TypeScript,Learning,KnowWhat", + github: "koustov", + cover: + "https://res.cloudinary.com/dgtdljyul/image/upload/v1651923177/ts_why_adazpf.png", + blog: "", + video: "", + language: "ts", + featured: true, + }, + { + id: "pl-memory-game", + name: "Memory Game", + description: "simple memory game or memory testing game build with ReactJS", + component: () => { + return ; + }, + path: "/plays/memory-game", + level: "Advanced", + tags: "MemoryGame, CardGame, ReactJS", + github: "Angryman18", + cover: + "https://cdn.pixabay.com/photo/2017/01/03/16/42/klee-1949946_960_720.jpg", + blog: "", + video: "", + language: "js", featured: true, + }, + { + id: "pl-registration-form", + name: "Registration-Form", + description: "Registration form with form validation without any library", + component: () => { + return ; + }, + path: "/plays/registration-form", + level: "Beginner", + tags: "JSX,useState,Objects,map", + github: "Deepak8717", + cover: "", + blog: "", + video: "", + language: "js", }, //replace new play item here ]; diff --git a/src/plays/index.js b/src/plays/index.js index 9d3cd7944d..81a997d94d 100644 --- a/src/plays/index.js +++ b/src/plays/index.js @@ -1,21 +1,21 @@ -export { default as PageNotFound } from 'common/404/PageNotFound'; -export { default as Home } from 'common/home/Home'; -export { default as CurrentTimer } from 'plays/clock/CurrentTimer'; -export { default as CdTimerComp } from 'plays/date-time-counter/CdTimerComp'; -export { default as BasicTree } from 'plays/org-tree/BasicTree'; -export { default as MovieContainer } from 'plays/movies/MovieContainer'; -export { default as WhyReact } from 'plays/why-react/WhyReact'; -export { default as CounterApp } from 'plays/counter/CounterApp'; -export { default as States } from 'plays/states/States'; -export { default as SocialCard } from 'plays/social-card/SocialCard'; -export { default as RandomMemeGenerator } from 'plays/random-meme-generator/RandomMemeGenerator'; -export { default as Keeper } from 'plays/keeper/Keeper'; -export { default as ReactTodoApp } from 'plays/react-todo-app/ReactTodoApp'; -export { default as QuoteGenerator } from 'plays/quote-generator/QuoteGenerator'; -export { default as ExpandingCards } from 'plays/expanding-cards/ExpandingCards'; -export { default as AnalogClock } from 'plays/analog-clock/AnalogClock'; - -export { default as PasswordGenerator } from 'plays/password-generator/PasswordGenerator'; -export { default as WhyTypescript } from 'plays/why-typescript/WhyTypescript'; -export { default as NetlifyCardGame } from 'plays/memory-game/NetlifyCardGame'; +export { default as PageNotFound } from "common/404/PageNotFound"; +export { default as Home } from "common/home/Home"; +export { default as CurrentTimer } from "plays/clock/CurrentTimer"; +export { default as CdTimerComp } from "plays/date-time-counter/CdTimerComp"; +export { default as BasicTree } from "plays/org-tree/BasicTree"; +export { default as MovieContainer } from "plays/movies/MovieContainer"; +export { default as WhyReact } from "plays/why-react/WhyReact"; +export { default as CounterApp } from "plays/counter/CounterApp"; +export { default as States } from "plays/states/States"; +export { default as SocialCard } from "plays/social-card/SocialCard"; +export { default as RandomMemeGenerator } from "plays/random-meme-generator/RandomMemeGenerator"; +export { default as Keeper } from "plays/keeper/Keeper"; +export { default as ReactTodoApp } from "plays/react-todo-app/ReactTodoApp"; +export { default as QuoteGenerator } from "plays/quote-generator/QuoteGenerator"; +export { default as ExpandingCards } from "plays/expanding-cards/ExpandingCards"; +export { default as AnalogClock } from "plays/analog-clock/AnalogClock"; +export { default as PasswordGenerator } from "plays/password-generator/PasswordGenerator"; +export { default as WhyTypescript } from "plays/why-typescript/WhyTypescript"; +export { default as NetlifyCardGame } from "plays/memory-game/NetlifyCardGame"; +export { default as RegistrationForm } from "plays/registration-form/RegistrationForm"; //add export here diff --git a/src/plays/registration-form/Readme.md b/src/plays/registration-form/Readme.md new file mode 100644 index 0000000000..1aab4e546a --- /dev/null +++ b/src/plays/registration-form/Readme.md @@ -0,0 +1,13 @@ +# registration-form + +A Simple registration form with validation without any library + +## What will you learn ? + +- How to use `useState` hook +- working with objects and how to work `useState` with nested objects +- Learn validation in pure js + +The file `RegistrationForm.jsx` has all the code required to build this project! So, if you want to get a grasp of it, try implementing it on your own! + +Keep Learning diff --git a/src/plays/registration-form/Registration-form.css b/src/plays/registration-form/Registration-form.css new file mode 100644 index 0000000000..e9030c30a7 --- /dev/null +++ b/src/plays/registration-form/Registration-form.css @@ -0,0 +1,90 @@ +.registration-form-heading { + text-align: center; +} +.registration-form .container { + width: 90%; + margin: auto; + max-width: 700px; + text-align: center; +} +.registration-form .form-inside { + max-width: 400px; + margin: auto; + text-align: left; + background: #fff; + padding: 30px; + box-shadow: 2px 2px 11px 1px #ccc, -2px -2px 16px 1px #ccc; + border-radius: 10px; +} +.registration-form .user-input { + display: flex; + flex-direction: column; + margin: 10px auto; +} +.registration-form .user-label { + text-transform: capitalize; + margin: 2px 0; +} +.registration-form .user-input .registration-input { + padding: 5px; +} +.registration-form .user-btn { + text-align: center; + margin: 25px auto 0; +} +.registration-form .user-btn .btn { + width: 100px; + background: blueviolet; + padding: 5px; + color: #fff; + font-size: 20px; + border: none; + font-weight: bold; + border-radius: 5px; +} +.registration-form .hidden { + display: none; +} +.registration-form .error { + color: red; +} +.registration-form .input-error { + border: 1px solid red; +} +.registration-form .entered-value-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} +.registration-form .entered-value-container .success { + margin: 50px auto 10px; + font-size: 1.5em; + color: rgb(43, 227, 43); +} +.registration-form .entered-value-container .entered-value-inner { + display: flex; + width: 80%; + max-width: 700px; + justify-content: center; +} +.entered-value-row { + width: 100%; + margin: 5px; + text-align: center; +} + +.registration-form .entered-value-row .heading { + font-weight: bold; + text-transform: capitalize; + font-size: 1em; +} +@media screen and (max-width: 768px) { + .registration-form .entered-value-container .entered-value-inner { + flex-direction: column; + width: 90%; + } + .registration-form .entered-value-container .success { + margin: 10px auto; + } +} diff --git a/src/plays/registration-form/RegistrationForm.jsx b/src/plays/registration-form/RegistrationForm.jsx new file mode 100644 index 0000000000..d55cec4f33 --- /dev/null +++ b/src/plays/registration-form/RegistrationForm.jsx @@ -0,0 +1,238 @@ +import { getPlayById } from "meta/play-meta-util"; + +import PlayHeader from "common/playlists/PlayHeader"; +import "./Registration-form.css"; +import { useState } from "react"; + +function RegistrationForm(props) { + // Do not remove the below lines. + // The following code is to fetch the current play from the URL + const { id } = props; + const play = getPlayById(id); + + // Your Code Start below. + + //for input values + const [values, setValues] = useState({ + name: "", + email: "", + password: "", + confirmPassword: "", + errors: { + name: "", + email: "", + password: "", + confirmPassword: "", + }, + }); + + //input values after submit + const [storedValues, setStoredValues] = useState({ + name: "", + email: "", + password: "", + confirmPassword: "", + }); + + //self explanatory + const validateEmail = (emailvalue) => { + if (emailvalue[0] === "email") { + const regx = /\S+@\S+\.\S+/; + if (!regx.test(emailvalue[1])) { + //set error and return false + setValues((prevState) => ({ + ...prevState, + errors: { + ...prevState.errors, + email: `your email is not correct`, + }, + })); + return false; + } else { + //if email is correct clear error msg and return true + setValues((prevState) => ({ + ...prevState, + errors: { + ...prevState.errors, + email: ``, + }, + })); + return true; + } + } + //if other than email just return true + return true; + }; + + //self explanatory + const conmparePassword = (pass, confirmPass) => { + // (pass and confirmPass) arrays containing name, value pair eg. ["password","xyz"] and ["confirmpassword","xyz"] + if (pass[0] === "password") { + if (values.confirmPassword !== "") { + if (pass[1] !== confirmPass[1]) { + //values don't match set error, return false + setValues((prevState) => ({ + ...prevState, + errors: { + ...prevState.errors, + password: "Password doesn't match", + confirmPassword: "Password doesn't match", + }, + })); + return false; + } else { + //if matches remove error msgs, return true + setValues((prevState) => ({ + ...prevState, + errors: { + ...prevState.errors, + password: "", + confirmPassword: "", + }, + })); + return true; + } + } + //return true in any other condition + return true; + } + //return true in any other condition + return true; + }; + + //final validation + const validate = (value) => { + //value is an array ex. ["name","deepak"] + if (value[1] === "") { + //if empty set error msg return false + setValues((prevState) => ({ + ...prevState, + errors: { + ...prevState.errors, + [value[0]]: `Please fill the ${value[0]}`, + }, + })); + return false; + } else { + //if not empty remove message return (validate email && compare password) + setValues((prevState) => ({ + ...prevState, + errors: { + ...prevState.errors, + [value[0]]: ``, + }, + })); + return ( + validateEmail(value) && + conmparePassword(value, Object.entries(values)[3]) + ); + } + }; + + const submitHandler = (e) => { + e.preventDefault(); + let counter = 0; + Object.entries(values).forEach((value, i) => { + //validate each field, increase counter if validation passed. + if (validate(value)) { + counter = counter + 1; + } + }); + + //if all fields are validated succsfully store the values in new object and clear the input values + if (counter === 5) { + setStoredValues({ + ...storedValues, + name: values.name, + email: values.email, + password: values.password, + confirmPassword: values.confirmPassword, + }); + setValues({ + name: "", + email: "", + password: "", + confirmPassword: "", + errors: { name: "", email: "", password: "", confirmPassword: "" }, + }); + } + }; + + const onChangeHandler = (e) => { + const { name, value } = e.target; + setValues({ ...values, [name]: value }); + }; + + return ( + <> +
+ +
+ {/* Your Code Starts Here */} +

+ A Registration form validation without any library +

+
+
+ {Object.entries(values).map((value, index) => { + return ( +
+ + + +
+ ); + })} +
+ +
+
+
+ Form Submitted ! +
+ {Object.entries(storedValues).map((storedValue, index) => { + return ( +
+
{storedValue[0]}
+
{storedValue[1]}
+
+ ); + })} +
+
+
+ {/* Your Code Ends Here */} +
+
+ + ); +} + +export default RegistrationForm; diff --git a/src/plays/registration-form/cover.png b/src/plays/registration-form/cover.png new file mode 100644 index 0000000000..e6be607a04 Binary files /dev/null and b/src/plays/registration-form/cover.png differ