Skip to content

Commit ee510ba

Browse files
committed
completed section
1 parent ec09227 commit ee510ba

23 files changed

+307
-99
lines changed

public/favicon.ico

-3.78 KB
Binary file not shown.

public/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
</head>
2929
<body>
3030
<noscript>You need to enable JavaScript to run this app.</noscript>
31+
<div id="backdrop-root"></div>
32+
<div id="overlay-root"></div>
3133
<div id="root"></div>
3234
<!--
3335
This HTML file is a template.

public/logo192.png

-5.22 KB
Binary file not shown.

public/logo512.png

-9.44 KB
Binary file not shown.

src/App.css

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,12 @@
1-
.App {
2-
text-align: center;
1+
* {
2+
box-sizing: border-box;
33
}
44

5-
.App-logo {
6-
height: 40vmin;
7-
pointer-events: none;
5+
html {
6+
font-family: sans-serif;
7+
background: #1f1f1f
88
}
99

10-
@media (prefers-reduced-motion: no-preference) {
11-
.App-logo {
12-
animation: App-logo-spin infinite 20s linear;
13-
}
14-
}
15-
16-
.App-header {
17-
background-color: #282c34;
18-
min-height: 100vh;
19-
display: flex;
20-
flex-direction: column;
21-
align-items: center;
22-
justify-content: center;
23-
font-size: calc(10px + 2vmin);
24-
color: white;
25-
}
26-
27-
.App-link {
28-
color: #61dafb;
29-
}
30-
31-
@keyframes App-logo-spin {
32-
from {
33-
transform: rotate(0deg);
34-
}
35-
to {
36-
transform: rotate(360deg);
37-
}
38-
}
10+
body {
11+
margin: 0;
12+
}

src/App.js

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
1-
import logo from './logo.svg';
1+
import React, { useState, Fragment } from 'react';
22
import './App.css';
3+
import AddUser from './components/Users/AddUser';
4+
import UsersList from './components/Users/UsersList';
35

46
function App() {
7+
const [usersList, setusersList] = useState([]);
8+
9+
const addUserHandler = (uName, uAge) => {
10+
setusersList((prevUsersList) => {
11+
return [...prevUsersList, { name: uName, age: uAge, id: Math.random().toString() }];
12+
});
13+
}
14+
515
return (
6-
<div className="App">
7-
<header className="App-header">
8-
<img src={logo} className="App-logo" alt="logo" />
9-
<p>
10-
Edit <code>src/App.js</code> and save to reload.
11-
</p>
12-
<a
13-
className="App-link"
14-
href="https://reactjs.org"
15-
target="_blank"
16-
rel="noopener noreferrer"
17-
>
18-
Learn React
19-
</a>
20-
</header>
21-
</div>
16+
<Fragment>
17+
<AddUser onAddUser={addUserHandler} />
18+
<UsersList users={usersList} />
19+
</Fragment>
2220
);
2321
}
2422

src/App.test.js

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/components/Helpers/Wrapper.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const Wrapper = props => {
2+
return props.children;
3+
};
4+
5+
export default Wrapper;

src/components/UI/Button.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
import classes from './Button.module.css';
3+
4+
function Button(props) {
5+
return (
6+
<button
7+
className={classes.button}
8+
type={props.type || 'button'}
9+
onClick={props.onClick}>
10+
{props.children}
11+
</button>
12+
)
13+
}
14+
15+
export default Button

src/components/UI/Button.module.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
.button {
2+
font: inherit;
3+
border: 1px solid #4f005f;
4+
background: #4f005f;
5+
color: white;
6+
padding: 0.25rem 1rem;
7+
cursor: pointer;
8+
border-radius: 5px;
9+
}
10+
11+
.button:hover,
12+
.button:active {
13+
background: #741188;
14+
border-color: #741188;
15+
}
16+
17+
.button:focus {
18+
outline: none;
19+
}

src/components/UI/Card.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
import classes from './Card.module.css';
3+
4+
function Card(props) {
5+
return (
6+
<div className={`${classes.card} ${props.className}`}>
7+
{props.children}
8+
</div>
9+
)
10+
}
11+
12+
export default Card

src/components/UI/Card.module.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.card {
2+
background: white;
3+
box-shadow: 0 2px 8px rgba(0,0,0,0.26);
4+
border-radius: 10px;
5+
}

src/components/UI/ErrorModal.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from "react";
2+
import ReactDom from "react-dom";
3+
import Card from "./Card";
4+
import Button from "./Button";
5+
import classes from "./ErrorModal.module.css";
6+
7+
const Backdrop = (props) => {
8+
return <div className={classes.backdrop} onClick={props.onConfirm} />;
9+
};
10+
11+
const ModalOverlay = (props) => {
12+
return (
13+
<Card className={classes.modal}>
14+
<header className={classes.header}>
15+
<h2>{props.title}</h2>
16+
</header>
17+
<div className={classes.content}>
18+
<p>{props.message}</p>
19+
</div>
20+
<footer className={classes.actions}>
21+
<Button onClick={props.onConfirm}>okay</Button>
22+
</footer>
23+
</Card>
24+
);
25+
};
26+
27+
function ErrorModal(props) {
28+
return (
29+
<React.Fragment>
30+
{ReactDom.createPortal(
31+
<Backdrop onConfirm={props.onConfirm} />,
32+
document.getElementById("backdrop-root")
33+
)}
34+
{ReactDom.createPortal(
35+
<ModalOverlay
36+
title={props.title}
37+
message={props.message}
38+
onConfirm={props.onConfirm}
39+
/>,
40+
document.getElementById("overlay-root")
41+
)}
42+
</React.Fragment>
43+
);
44+
}
45+
46+
export default ErrorModal;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
.backdrop {
2+
position: fixed;
3+
top: 0;
4+
left: 0;
5+
width: 100%;
6+
height: 100vh;
7+
z-index: 10;
8+
background: rgba(0, 0, 0, 0.75);
9+
}
10+
11+
.modal {
12+
position: fixed;
13+
top: 30vh;
14+
left: 10%;
15+
width: 80%;
16+
z-index: 100;
17+
overflow: hidden;
18+
}
19+
20+
.header {
21+
background: #4f005f;
22+
padding: 1rem;
23+
}
24+
25+
.header h2 {
26+
margin: 0;
27+
color: white;
28+
}
29+
30+
.content {
31+
padding: 1rem;
32+
}
33+
34+
.actions {
35+
padding: 1rem;
36+
display: flex;
37+
justify-content: flex-end;
38+
}
39+
40+
@media (min-width: 768px) {
41+
.modal {
42+
left: calc(50% - 20rem);
43+
width: 40rem;
44+
}
45+
}

src/components/Users/AddUser.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { React, useState, useRef } from "react";
2+
import Card from "../UI/Card";
3+
import classes from "./AddUser.module.css";
4+
import Button from "../UI/Button";
5+
import ErrorModal from "../UI/ErrorModal";
6+
import Wrapper from "../Helpers/Wrapper";
7+
8+
function AddUser(props) {
9+
const nameInputRef = useRef();
10+
const ageInputRef = useRef();
11+
12+
13+
const [error, setError] = useState();
14+
15+
const addUserHandler = (event) => {
16+
event.preventDefault();
17+
const enteredName = nameInputRef.current.value;
18+
const enteredUserAge = ageInputRef.current.value;
19+
if (enteredName.trim().length === 0 || enteredUserAge.trim().length === 0) {
20+
setError({
21+
title: "Invalid Input",
22+
message: "Please enter a valid name and age (non-empty values).",
23+
});
24+
return;
25+
}
26+
if (+enteredUserAge < 1) {
27+
setError({
28+
title: "Invalid Age",
29+
message: "Please enter a valid age (greater than 0).",
30+
});
31+
return;
32+
}
33+
props.onAddUser(enteredName, enteredUserAge);
34+
nameInputRef.current.value ='';
35+
ageInputRef.current.value ='';
36+
};
37+
38+
39+
const errorHandler = () => {
40+
setError(null);
41+
};
42+
43+
return (
44+
<Wrapper>
45+
{error && (
46+
<ErrorModal
47+
title={error.title}
48+
message={error.message}
49+
onConfirm={errorHandler}
50+
/>
51+
)}
52+
<Card className={classes.input}>
53+
<form onSubmit={addUserHandler} className="form">
54+
<label htmlFor="username">Username</label>
55+
<input
56+
id="username"
57+
type="text"
58+
ref={nameInputRef}
59+
/>
60+
<label htmlFor="age">Age (Years)</label>
61+
<input
62+
id="age"
63+
type="number"
64+
ref={ageInputRef}
65+
/>
66+
<Button type="submit">Add USer</Button>
67+
</form>
68+
</Card>
69+
</Wrapper>
70+
);
71+
}
72+
73+
export default AddUser;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.input {
2+
margin: 2rem auto;
3+
padding: 1rem;
4+
width: 90%;
5+
max-width: 40rem;
6+
}
7+
8+
.input label {
9+
display: block;
10+
font-weight: bold;
11+
margin-bottom: 0.5rem;
12+
}
13+
14+
.input input {
15+
font: inherit;
16+
display: block;
17+
width: 100%;
18+
border: 1px solid #ccc;
19+
padding: 0.15rem;
20+
margin-bottom: 0.5rem;
21+
}
22+
23+
.input input:focus {
24+
outline: none;
25+
border-color: #4f005f;
26+
}

src/components/Users/UsersList.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from "react";
2+
import Card from "../UI/Card";
3+
import classes from "./UsersList.module.css";
4+
5+
function UsersList(props) {
6+
return (
7+
<Card className={classes.users}>
8+
<ul>
9+
{props.users.map((user) => (
10+
<li key={user.id}>
11+
{user.name} ({user.age} years old)
12+
</li>
13+
))}
14+
</ul>
15+
</Card>
16+
);
17+
}
18+
19+
export default UsersList;

0 commit comments

Comments
 (0)