├── 10_MINI_BLOG
└── miniblog
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ ├── Footer.js
│ ├── Footer.module.css
│ ├── Navbar.js
│ ├── Navbar.module.css
│ ├── PostDetail.js
│ └── PostDetail.module.css
│ ├── contexts
│ └── AuthContext.js
│ ├── firebase
│ └── config.js
│ ├── hooks
│ ├── useAuthentication.js
│ ├── useDeleteDocument.js
│ ├── useFetchDocument.js
│ ├── useFetchDocuments.js
│ ├── useInsertDocument.js
│ ├── useQuery.js
│ └── useUpdateDocument.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── pages
│ ├── About
│ │ ├── About.js
│ │ └── About.module.css
│ ├── CreatePost
│ │ ├── CreatePost.js
│ │ └── CreatePost.module.css
│ ├── Dashboard
│ │ ├── Dashboard.js
│ │ └── Dashboard.module.css
│ ├── EditPost
│ │ ├── EditPost.js
│ │ └── EditPost.module.css
│ ├── Home
│ │ ├── Home.js
│ │ └── Home.module.css
│ ├── Login
│ │ ├── Login.js
│ │ └── Login.module.css
│ ├── Post
│ │ ├── Post.js
│ │ └── Post.module.css
│ ├── Register
│ │ ├── Register.js
│ │ └── Register.module.css
│ └── Search
│ │ ├── Search.js
│ │ └── Search.module.css
│ ├── reportWebVitals.js
│ └── setupTests.js
├── 11_REACT_HOOKS
└── reacthooks
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ ├── HookCustom.js
│ ├── HookUseCallback.js
│ ├── HookUseContext.js
│ ├── HookUseEffect.js
│ ├── HookUseEffectLayout.js
│ ├── HookUseImperativeHandle.js
│ ├── HookUseMemo.js
│ ├── HookUseReducer.js
│ ├── HookUseRef.js
│ ├── HookUseState.js
│ ├── List.js
│ └── SomeComponent.js
│ ├── hooks
│ └── usePrevious.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── pages
│ ├── About.js
│ └── Home.js
│ ├── reportWebVitals.js
│ └── setupTests.js
├── 12_REACTGRAM
├── backend
│ ├── .env
│ ├── .gitignore
│ ├── app.js
│ ├── config
│ │ └── db.js
│ ├── controllers
│ │ ├── PhotoController.js
│ │ └── UserController.js
│ ├── middlewares
│ │ ├── authGuard.js
│ │ ├── handleValidations.js
│ │ ├── imageUpload.js
│ │ ├── photoValidations.js
│ │ └── userValidations.js
│ ├── models
│ │ ├── Photo.js
│ │ └── User.js
│ ├── package-lock.json
│ ├── package.json
│ ├── routes
│ │ ├── PhotoRoutes.js
│ │ ├── Router.js
│ │ └── UserRoutes.js
│ └── uploads
│ │ ├── photos
│ │ └── .gitkeep
│ │ └── users
│ │ └── .gitkeep
├── frontend
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── robots.txt
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── components
│ │ ├── Footer.css
│ │ ├── Footer.js
│ │ ├── LikeContainer.css
│ │ ├── LikeContainer.js
│ │ ├── Message.css
│ │ ├── Message.js
│ │ ├── Navbar.css
│ │ ├── Navbar.js
│ │ ├── PhotoItem.css
│ │ └── PhotoItem.js
│ │ ├── hooks
│ │ ├── useAuth.js
│ │ ├── useQuery.js
│ │ └── useResetComponentMessage.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── pages
│ │ ├── Auth
│ │ │ ├── Auth.css
│ │ │ ├── Login.js
│ │ │ └── Register.js
│ │ ├── EditProfile
│ │ │ ├── EditProfile.css
│ │ │ └── EditProfile.js
│ │ ├── Home
│ │ │ ├── Home.css
│ │ │ └── Home.js
│ │ ├── Photo
│ │ │ ├── Photo.css
│ │ │ └── Photo.js
│ │ ├── Profile
│ │ │ ├── Profile.css
│ │ │ └── Profile.js
│ │ └── Search
│ │ │ ├── Search.css
│ │ │ └── Search.js
│ │ ├── reportWebVitals.js
│ │ ├── services
│ │ ├── authService.js
│ │ ├── photoService.js
│ │ └── userService.js
│ │ ├── setupTests.js
│ │ ├── slices
│ │ ├── authSlice.js
│ │ ├── photoSlice.js
│ │ └── userSlice.js
│ │ ├── store.js
│ │ └── utils
│ │ └── config.js
└── images
│ ├── f1.jpg
│ ├── f2.jpg
│ ├── f3.jpg
│ ├── f4.jpg
│ ├── f5.jpg
│ ├── f6.jpg
│ ├── f7.jpg
│ ├── f8.jpg
│ ├── f9.jpg
│ ├── u1.jpg
│ ├── u2.jpg
│ └── u3.jpg
├── 13_CURSO_GRATUITO
└── todo
│ ├── .gitignore
│ ├── README.md
│ ├── data
│ └── db.json
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── 2_FUNDAMENTOS
└── fundamentos
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ ├── Challenge.js
│ ├── Events.js
│ ├── FirstComponent.js
│ ├── MyComponent.js
│ └── TemplateExpressions.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── 3_AVANCANDO_NO_REACT
└── avancando
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── img1.jpg
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── assets
│ └── city.jpg
│ ├── components
│ ├── CarDetails.js
│ ├── ChangeMessageState.js
│ ├── ConditionalRender.js
│ ├── Container.js
│ ├── ExecuteFunction.js
│ ├── Fragment.js
│ ├── ListRender.js
│ ├── ManageData.js
│ ├── MessageState.js
│ └── ShowUserName.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── 4_CSS_REACT
├── challengecss
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── robots.txt
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── components
│ │ ├── Car.js
│ │ └── Car.module.css
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── reportWebVitals.js
│ │ └── setupTests.js
└── css
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ ├── MyComponent.css
│ ├── MyComponent.js
│ ├── Title.js
│ └── Title.module.css
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── 5_FORM_EM_REACT
└── forms
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ ├── MyForm.css
│ └── MyForm.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── 6_PROJETO_SECRET_WORD
└── secretword
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ ├── Game.css
│ ├── Game.js
│ ├── GameOver.css
│ ├── GameOver.js
│ ├── StartScreen.css
│ └── StartScreen.js
│ ├── data
│ └── words.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── 7_REQ_HTTP_REACT
└── httpreact
│ ├── .gitignore
│ ├── README.md
│ ├── data
│ ├── db.json
│ └── db.json.backup
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── hooks
│ └── useFetch.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── 8_REACT_ROUTER
└── reactrouter
│ ├── .gitignore
│ ├── README.md
│ ├── data
│ ├── db.json
│ └── db.json.backup
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ ├── Navbar.css
│ ├── Navbar.js
│ └── SearchForm.js
│ ├── hooks
│ └── useFetch.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── pages
│ ├── About.js
│ ├── Home.css
│ ├── Home.js
│ ├── Info.js
│ ├── NotFound.js
│ ├── Product.js
│ └── Search.js
│ ├── reportWebVitals.js
│ └── setupTests.js
└── 9_CONTEXT
└── context
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── components
├── ChangeCounter.js
├── Navbar.css
└── Navbar.js
├── context
├── CounterContext.js
└── TitleColorContext.js
├── hooks
├── useCounterContext.js
└── useTitleColorContext.js
├── index.css
├── index.js
├── logo.svg
├── pages
├── About.js
├── Home.css
├── Home.js
└── Products.js
├── reportWebVitals.js
└── setupTests.js
/10_MINI_BLOG/miniblog/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "miniblog",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.3",
8 | "@testing-library/user-event": "^13.5.0",
9 | "firebase": "^9.6.7",
10 | "react": "^17.0.2",
11 | "react-dom": "^17.0.2",
12 | "react-router-dom": "^6.2.1",
13 | "react-scripts": "5.0.0",
14 | "web-vitals": "^2.1.4"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": [
24 | "react-app",
25 | "react-app/jest"
26 | ]
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/10_MINI_BLOG/miniblog/public/favicon.ico
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/10_MINI_BLOG/miniblog/public/logo192.png
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/10_MINI_BLOG/miniblog/public/logo512.png
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/App.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 60vh;
3 | margin-bottom: 5em;
4 | }
5 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import styles from "./Footer.module.css";
2 |
3 | const Footer = () => {
4 | return (
5 |
9 | );
10 | };
11 |
12 | export default Footer;
13 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/components/Footer.module.css:
--------------------------------------------------------------------------------
1 | .footer {
2 | height: 250px;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | justify-content: center;
7 | background-color: #edf3f6;
8 | }
9 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/components/Navbar.module.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | display: flex;
3 | box-shadow: rgba(0, 0, 0, 0.15) 0px -2px 10px 0px;
4 | justify-content: space-between;
5 | align-items: center;
6 | padding: 0.5em 2em;
7 | }
8 |
9 | .brand {
10 | font-size: 1.2em;
11 | }
12 |
13 | .brand span {
14 | font-weight: 900;
15 | text-transform: uppercase;
16 | }
17 |
18 | .links_list {
19 | display: flex;
20 | list-style: none;
21 | }
22 |
23 | .links_list li {
24 | margin-right: 1em;
25 | }
26 |
27 | .links_list li a {
28 | padding: 0.4em 0.6em;
29 | }
30 |
31 | .active {
32 | background-color: #000;
33 | color: #fff;
34 | }
35 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/components/PostDetail.js:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | import styles from "./PostDetail.module.css";
4 |
5 | const PostDetail = ({ post }) => {
6 | return (
7 |
8 |

9 |
{post.title}
10 |
por: {post.createdBy}
11 |
12 | {post.tags.map((tag) => (
13 |
14 | #
15 | {tag}
16 |
17 | ))}
18 |
19 |
20 | Ler
21 |
22 |
23 | );
24 | };
25 |
26 | export default PostDetail;
27 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/components/PostDetail.module.css:
--------------------------------------------------------------------------------
1 | .post_detail {
2 | margin-bottom: 2em;
3 | }
4 |
5 | .post_detail img {
6 | max-width: 600px;
7 | }
8 |
9 | .post_detail h2 {
10 | margin-bottom: 0.4em;
11 | }
12 |
13 | .post_detail .tags {
14 | margin-bottom: 1.2em;
15 | display: flex;
16 | }
17 |
18 | .tags p {
19 | margin-right: 0.5em;
20 | }
21 |
22 | .tags span {
23 | font-weight: bold;
24 | }
25 |
26 | .createdby {
27 | font-style: italic;
28 | color: #444;
29 | font-size: 0.8em;
30 | margin-bottom: 1em;
31 | }
32 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/contexts/AuthContext.js:
--------------------------------------------------------------------------------
1 | import { useContext, createContext } from "react";
2 |
3 | const AuthContext = createContext();
4 |
5 | export function AuthProvider({ children, value }) {
6 | return {children};
7 | }
8 |
9 | export function useAuthValue() {
10 | return useContext(AuthContext);
11 | }
12 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/firebase/config.js:
--------------------------------------------------------------------------------
1 | import { initializeApp } from "firebase/app";
2 | import { getFirestore } from "firebase/firestore";
3 |
4 | const firebaseConfig = {
5 | apiKey: "AIzaSyCLvEGhcQo1QjbHP-0D7bRaKrFAaGQW_sg",
6 | authDomain: "miniblog-ref.firebaseapp.com",
7 | projectId: "miniblog-ref",
8 | storageBucket: "miniblog-ref.appspot.com",
9 | messagingSenderId: "411532945241",
10 | appId: "1:411532945241:web:3808bd6c6bf5b1fb774e48",
11 | };
12 |
13 | const app = initializeApp(firebaseConfig);
14 |
15 | const db = getFirestore(app);
16 |
17 | export { db };
18 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/hooks/useDeleteDocument.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useReducer } from "react";
2 | import { db } from "../firebase/config";
3 | import { doc, deleteDoc } from "firebase/firestore";
4 |
5 | const initialState = {
6 | loading: null,
7 | error: null,
8 | };
9 |
10 | const deleteReducer = (state, action) => {
11 | switch (action.type) {
12 | case "LOADING":
13 | return { loading: true, error: null };
14 | case "DELETED_DOC":
15 | return { loading: false, error: null };
16 | case "ERROR":
17 | return { loading: false, error: action.payload };
18 | default:
19 | return state;
20 | }
21 | };
22 |
23 | export const useDeleteDocument = (docCollection) => {
24 | const [response, dispatch] = useReducer(deleteReducer, initialState);
25 |
26 | // deal with memory leak
27 | const [cancelled, setCancelled] = useState(false);
28 |
29 | const checkCancelBeforeDispatch = (action) => {
30 | if (!cancelled) {
31 | dispatch(action);
32 | }
33 | };
34 |
35 | const deleteDocument = async (id) => {
36 | checkCancelBeforeDispatch({ type: "LOADING" });
37 |
38 | try {
39 | const deletedDocument = await deleteDoc(doc(db, docCollection, id));
40 |
41 | checkCancelBeforeDispatch({
42 | type: "DELETED_DOC",
43 | payload: deletedDocument,
44 | });
45 | } catch (error) {
46 | checkCancelBeforeDispatch({ type: "ERROR", payload: error.message });
47 | }
48 | };
49 |
50 | useEffect(() => {
51 | return () => setCancelled(true);
52 | }, []);
53 |
54 | return { deleteDocument, response };
55 | };
56 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/hooks/useFetchDocument.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { db } from "../firebase/config";
3 | import { doc, getDoc } from "firebase/firestore";
4 |
5 | export const useFetchDocument = (docCollection, id) => {
6 | const [document, setDocument] = useState(null);
7 | const [error, setError] = useState(null);
8 | const [loading, setLoading] = useState(null);
9 |
10 | useEffect(() => {
11 | const loadDocument = async () => {
12 | setLoading(true);
13 |
14 | try {
15 | const docRef = await doc(db, docCollection, id);
16 | const docSnap = await getDoc(docRef);
17 |
18 | setDocument(docSnap.data());
19 | } catch (error) {
20 | console.log(error);
21 | setError(error.message);
22 | }
23 |
24 | setLoading(false);
25 | };
26 |
27 | loadDocument();
28 | }, [docCollection, id]);
29 |
30 | console.log(document);
31 |
32 | return { document, loading, error };
33 | };
34 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/hooks/useInsertDocument.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useReducer } from "react";
2 | import { db } from "../firebase/config";
3 | import { collection, addDoc, Timestamp } from "firebase/firestore";
4 |
5 | const initialState = {
6 | loading: null,
7 | error: null,
8 | };
9 |
10 | const insertReducer = (state, action) => {
11 | switch (action.type) {
12 | case "LOADING":
13 | return { loading: true, error: null };
14 | case "INSERTED_DOC":
15 | return { loading: false, error: null };
16 | case "ERROR":
17 | return { loading: false, error: action.payload };
18 | default:
19 | return state;
20 | }
21 | };
22 |
23 | export const useInsertDocument = (docCollection) => {
24 | const [response, dispatch] = useReducer(insertReducer, initialState);
25 |
26 | // deal with memory leak
27 | const [cancelled, setCancelled] = useState(false);
28 |
29 | const checkCancelBeforeDispatch = (action) => {
30 | if (!cancelled) {
31 | dispatch(action);
32 | }
33 | };
34 |
35 | const insertDocument = async (document) => {
36 | checkCancelBeforeDispatch({ type: "LOADING" });
37 |
38 | try {
39 | const newDocument = { ...document, createdAt: Timestamp.now() };
40 |
41 | const insertedDocument = await addDoc(
42 | collection(db, docCollection),
43 | newDocument
44 | );
45 |
46 | checkCancelBeforeDispatch({
47 | type: "INSERTED_DOC",
48 | payload: insertedDocument,
49 | });
50 | } catch (error) {
51 | checkCancelBeforeDispatch({ type: "ERROR", payload: error.message });
52 | }
53 | };
54 |
55 | useEffect(() => {
56 | return () => setCancelled(true);
57 | }, []);
58 |
59 | return { insertDocument, response };
60 | };
61 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/hooks/useQuery.js:
--------------------------------------------------------------------------------
1 | import { useLocation } from "react-router-dom";
2 | import { useMemo } from "react";
3 |
4 | export function useQuery() {
5 | const { search } = useLocation();
6 |
7 | return useMemo(() => new URLSearchParams(search), [search]);
8 | }
9 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/hooks/useUpdateDocument.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useReducer } from "react";
2 | import { db } from "../firebase/config";
3 | import { updateDoc, doc } from "firebase/firestore";
4 |
5 | const initialState = {
6 | loading: null,
7 | error: null,
8 | };
9 |
10 | const updateReducer = (state, action) => {
11 | switch (action.type) {
12 | case "LOADING":
13 | return { loading: true, error: null };
14 | case "UPDATED_DOC":
15 | return { loading: false, error: null };
16 | case "ERROR":
17 | return { loading: false, error: action.payload };
18 | default:
19 | return state;
20 | }
21 | };
22 |
23 | export const useUpdateDocument = (docCollection) => {
24 | const [response, dispatch] = useReducer(updateReducer, initialState);
25 |
26 | // deal with memory leak
27 | const [cancelled, setCancelled] = useState(false);
28 |
29 | const checkCancelBeforeDispatch = (action) => {
30 | if (!cancelled) {
31 | dispatch(action);
32 | }
33 | };
34 |
35 | const updateDocument = async (uid, data) => {
36 | checkCancelBeforeDispatch({ type: "LOADING" });
37 |
38 | try {
39 | const docRef = await doc(db, docCollection, uid);
40 |
41 | console.log(docRef);
42 |
43 | const updatedDocument = await updateDoc(docRef, data);
44 |
45 | console.log(updateDocument);
46 |
47 | checkCancelBeforeDispatch({
48 | type: "UPDATED_DOC",
49 | payload: updatedDocument,
50 | });
51 | } catch (error) {
52 | console.log(error);
53 | checkCancelBeforeDispatch({ type: "ERROR", payload: error.message });
54 | }
55 | };
56 |
57 | useEffect(() => {
58 | return () => setCancelled(true);
59 | }, []);
60 |
61 | return { updateDocument, response };
62 | };
63 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/About/About.js:
--------------------------------------------------------------------------------
1 | import styles from "./About.module.css";
2 |
3 | import { Link } from "react-router-dom";
4 |
5 | const About = () => {
6 | return (
7 |
8 |
9 | Sobre o Mini Blog
10 |
11 |
12 | Este projeto consiste em um blog feito com React no front-end e Firebase
13 | no back-end.
14 |
15 |
16 | Criar post
17 |
18 |
19 | );
20 | };
21 |
22 | export default About;
23 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/About/About.module.css:
--------------------------------------------------------------------------------
1 | .about {
2 | text-align: center;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | }
7 |
8 | .about p {
9 | color: #aaa;
10 | margin-bottom: 2em;
11 | }
12 |
13 | .about a {
14 | display: block;
15 | margin-top: 15px;
16 | padding: 10px 15px;
17 | }
18 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/CreatePost/CreatePost.module.css:
--------------------------------------------------------------------------------
1 | .create_post {
2 | text-align: center;
3 | }
4 |
5 | .create_post h2 {
6 | font-size: 2.2em;
7 | }
8 |
9 | .create_post p {
10 | color: #aaa;
11 | margin-bottom: 2em;
12 | }
13 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/Dashboard/Dashboard.module.css:
--------------------------------------------------------------------------------
1 | .dashboard {
2 | text-align: center;
3 | display: flex;
4 | flex-direction: column;
5 | justify-content: center;
6 | align-items: center;
7 | }
8 |
9 | .dashboard h2 {
10 | font-size: 2.2em;
11 | margin-bottom: 0.5em;
12 | }
13 |
14 | .dashboard p {
15 | color: #aaa;
16 | margin-bottom: 1em;
17 | }
18 |
19 | .noposts {
20 | text-align: center;
21 | }
22 |
23 | .noposts p {
24 | margin-bottom: 1.5em;
25 | }
26 |
27 | .noposts a {
28 | padding: 10px 25px;
29 | }
30 |
31 | .post_header {
32 | display: flex;
33 | justify-content: space-between;
34 | font-weight: bold;
35 | border-bottom: 2px solid #ccc;
36 | width: 80%;
37 | padding: 10px;
38 | }
39 |
40 | .post_row {
41 | display: flex;
42 | justify-content: space-between;
43 | align-items: center;
44 | border-bottom: 1px solid #eee;
45 | width: 80%;
46 | padding: 10px;
47 | }
48 |
49 | .post_row p {
50 | color: #000;
51 | }
52 |
53 | .post_row button,
54 | .post_row a {
55 | margin: 0 5px;
56 | height: 30px;
57 | width: 100px;
58 | font-size: 0.7em;
59 | }
60 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/EditPost/EditPost.module.css:
--------------------------------------------------------------------------------
1 | .edit_post {
2 | text-align: center;
3 | }
4 |
5 | .edit_post h2 {
6 | font-size: 2.2em;
7 | }
8 |
9 | .edit_post p {
10 | color: #aaa;
11 | margin-bottom: 2em;
12 | }
13 |
14 | .edit_post .preview_title {
15 | margin-bottom: 0.2em;
16 | color: #000;
17 | font-weight: bold;
18 | }
19 |
20 | .image_preview {
21 | max-width: 100%;
22 | margin-bottom: 1em;
23 | }
24 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/Home/Home.js:
--------------------------------------------------------------------------------
1 | // CSS
2 | import styles from "./Home.module.css";
3 |
4 | // hooks
5 | import { useFetchDocuments } from "../../hooks/useFetchDocuments";
6 | import { useNavigate, Link } from "react-router-dom";
7 |
8 | // react
9 | import { useState } from "react";
10 |
11 | // components
12 | import PostDetail from "../../components/PostDetail";
13 |
14 | const Home = () => {
15 | const { documents: posts, loading } = useFetchDocuments("posts");
16 |
17 | const navigate = useNavigate();
18 |
19 | const [query, setQuery] = useState("");
20 |
21 | const handleSubmit = (e) => {
22 | e.preventDefault();
23 |
24 | if (query) {
25 | return navigate(`/search?q=${query}`);
26 | }
27 | };
28 |
29 | console.log(loading);
30 |
31 | return (
32 |
33 |
Veja os nossos posts mais recentes
34 |
42 |
43 | {loading &&
Carregando...
}
44 | {posts && posts.length === 0 && (
45 |
46 |
Não foram encontrados posts
47 |
48 | Criar primeiro post
49 |
50 |
51 | )}
52 | {posts && posts.map((post) =>
)}
53 |
54 |
55 | );
56 | };
57 |
58 | export default Home;
59 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/Home/Home.module.css:
--------------------------------------------------------------------------------
1 | .home {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | justify-content: center;
6 | }
7 |
8 | .search_form {
9 | max-width: 100%;
10 | width: 60%;
11 | display: flex;
12 | justify-content: center;
13 | margin-bottom: 2em;
14 | }
15 |
16 | .search_form input {
17 | margin-right: 10px;
18 | width: 50%;
19 | }
20 |
21 | .noposts {
22 | text-align: center;
23 | }
24 |
25 | .noposts p {
26 | margin-bottom: 1.5em;
27 | }
28 |
29 | .noposts a {
30 | padding: 10px 25px;
31 | }
32 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/Login/Login.module.css:
--------------------------------------------------------------------------------
1 | .login {
2 | text-align: center;
3 | }
4 |
5 | .login p {
6 | color: #aaa;
7 | }
8 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/Post/Post.js:
--------------------------------------------------------------------------------
1 | // CSS
2 | import styles from "./Post.module.css";
3 |
4 | // hooks
5 | import { useFetchDocument } from "../../hooks/useFetchDocument";
6 | import { useParams } from "react-router-dom";
7 |
8 | const Post = () => {
9 | const { id } = useParams();
10 | const { document: post } = useFetchDocument("posts", id);
11 |
12 | return (
13 |
14 | {post && (
15 | <>
16 |
{post.title}
17 |

18 |
{post.body}
19 |
Este post trata sobre:
20 |
21 | {post.tags.map((tag) => (
22 |
23 | #
24 | {tag}
25 |
26 | ))}
27 |
28 | >
29 | )}
30 |
31 | );
32 | };
33 |
34 | export default Post;
35 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/Post/Post.module.css:
--------------------------------------------------------------------------------
1 | .post_container {
2 | text-align: center;
3 | }
4 |
5 | .post_container h3 {
6 | margin-bottom: 0.2em;
7 | }
8 |
9 | .post_container .tags {
10 | display: flex;
11 | justify-content: center;
12 | }
13 |
14 | .tags p {
15 | margin-right: 1em;
16 | }
17 |
18 | .tags span {
19 | font-weight: bold;
20 | }
21 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/Register/Register.module.css:
--------------------------------------------------------------------------------
1 | .register {
2 | text-align: center;
3 | }
4 |
5 | .register p {
6 | color: #aaa;
7 | }
8 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/Search/Search.js:
--------------------------------------------------------------------------------
1 | import styles from "./Search.module.css";
2 |
3 | // hooks
4 | import { useFetchDocuments } from "../../hooks/useFetchDocuments";
5 | import { useQuery } from "../../hooks/useQuery";
6 |
7 | // components
8 | import PostDetail from "../../components/PostDetail";
9 | import { Link } from "react-router-dom";
10 |
11 | const Search = () => {
12 | const query = useQuery();
13 | const search = query.get("q");
14 |
15 | const { documents: posts } = useFetchDocuments("posts", search);
16 |
17 | return (
18 |
19 |
Resultados encontrados para: {search}
20 |
21 | {posts && posts.length === 0 && (
22 | <>
23 |
Não foram encontrados posts a partir da sua busca...
24 |
25 | Voltar
26 |
27 | >
28 | )}
29 | {posts && posts.map((post) =>
)}
30 |
31 |
32 | );
33 | };
34 |
35 | export default Search;
36 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/pages/Search/Search.module.css:
--------------------------------------------------------------------------------
1 | .search_container {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | justify-content: center;
6 | }
7 |
8 | .search_container h1 {
9 | margin-bottom: 1em;
10 | }
11 |
12 | .search_container p {
13 | margin-bottom: 30px;
14 | }
15 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/10_MINI_BLOG/miniblog/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reacthooks",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.4",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-router-dom": "^6.2.2",
12 | "react-scripts": "5.0.0",
13 | "web-vitals": "^2.1.4"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": [
23 | "react-app",
24 | "react-app/jest"
25 | ]
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/11_REACT_HOOKS/reacthooks/public/favicon.ico
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/11_REACT_HOOKS/reacthooks/public/logo192.png
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/11_REACT_HOOKS/reacthooks/public/logo512.png
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
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 | }
39 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/App.js:
--------------------------------------------------------------------------------
1 | import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
2 |
3 | import "./App.css";
4 |
5 | import About from "./pages/About";
6 | import Home from "./pages/Home";
7 |
8 | import { HookUseContext } from "./components/HookUseContext";
9 |
10 | function App() {
11 | return (
12 |
13 |
14 | React Hooks
15 |
16 |
17 |
18 | -
19 | Home
20 |
21 | -
22 | About
23 |
24 |
25 |
26 | } />
27 | } />
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
35 | export default App;
36 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/HookCustom.js:
--------------------------------------------------------------------------------
1 | import { useDebugValue, useState } from "react";
2 | import { usePrevious } from "../hooks/usePrevious";
3 |
4 | const HookCustom = () => {
5 | const [number, setNumber] = useState(0);
6 | const previousValue = usePrevious(number);
7 |
8 | return (
9 |
10 |
Custom Hook
11 |
Atual: {number}
12 |
Anterior: {previousValue}
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default HookCustom;
20 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/HookUseCallback.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from "react";
2 | import List from "./List";
3 |
4 | const HookUseCallback = () => {
5 | const [counter, setCounter] = useState(0);
6 |
7 | // const getItemsFromDatabase = () => {
8 | // return ["a", "b", "c"];
9 | // };
10 |
11 | const getItemsFromDatabase = useCallback(() => {
12 | return ["a", "b", "c"];
13 | }, []);
14 |
15 | return (
16 |
17 |
useCallback
18 |
19 |
20 |
{counter}
21 |
22 |
23 | );
24 | };
25 |
26 | export default HookUseCallback;
27 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/HookUseContext.js:
--------------------------------------------------------------------------------
1 | import { createContext } from "react";
2 |
3 | export const SomeContext = createContext();
4 |
5 | export const HookUseContext = ({ children }) => {
6 | const contextValue = "testing context";
7 |
8 | return (
9 |
10 | {children}
11 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/HookUseEffect.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | const HookUseEffect = () => {
4 | // 1 - useEffect, sem dependencias
5 | // executa sempre ao renderizar componente
6 | useEffect(() => {
7 | console.log("Estou sendo executado!");
8 | });
9 |
10 | const [number, setNumber] = useState(1);
11 |
12 | const changeSomething = () => {
13 | setNumber(number + 1);
14 | };
15 |
16 | // 2 - array de dependências vazio
17 | useEffect(() => {
18 | console.log("Serei executado apenas uma vez!");
19 | }, []);
20 |
21 | // 3 - item no array de dependências
22 | const [anotherNumber, setAnotherNumber] = useState(0);
23 |
24 | useEffect(() => {
25 | console.log("Sou executado apenas quando anotherNumber muda!");
26 | }, [anotherNumber]);
27 |
28 | // 4 - limpeza do useEffect
29 | useEffect(() => {
30 | const timer = setTimeout(() => {
31 | console.log("Hello World!");
32 |
33 | // gera erro sem cleanup
34 | //setAnotherNumber(anotherNumber + 1);
35 | }, 2000);
36 |
37 | // realizar exemplo sem clean up
38 | return () => clearTimeout(timer);
39 | }, [anotherNumber]);
40 |
41 | return (
42 |
43 |
useEffect
44 |
Number: {number}
45 |
46 |
47 |
Another Number: {anotherNumber}
48 |
51 |
52 |
53 | );
54 | };
55 |
56 | export default HookUseEffect;
57 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/HookUseEffectLayout.js:
--------------------------------------------------------------------------------
1 | import { useLayoutEffect, useEffect, useState } from "react";
2 |
3 | const HookUseEffectLayout = () => {
4 | const [name, setName] = useState("Algum nome");
5 |
6 | useEffect(() => {
7 | console.log("2");
8 | setName("Mudou de novo!");
9 | }, []);
10 |
11 | useLayoutEffect(() => {
12 | console.log("1");
13 | setName("Outro nome");
14 | }, []);
15 |
16 | console.log(name);
17 |
18 | return (
19 |
20 |
useEffectLayout
21 |
Nome: {name}
22 |
23 |
24 | );
25 | };
26 |
27 | export default HookUseEffectLayout;
28 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/HookUseImperativeHandle.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import SomeComponent from "./SomeComponent";
3 |
4 | const HookUseImperativeHandle = () => {
5 | const inputRef = useRef();
6 |
7 | return (
8 |
9 |
useImperativeHandle
10 |
11 |
12 |
13 |
14 | );
15 | };
16 |
17 | export default HookUseImperativeHandle;
18 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/HookUseMemo.js:
--------------------------------------------------------------------------------
1 | import { useState, useMemo, useEffect } from "react";
2 |
3 | const HookUseMemo = () => {
4 | const [number, setNumber] = useState(0);
5 |
6 | // gera o erro
7 | // const premiumNumbers = ["0", "100", "200"];
8 |
9 | // resolvendo com o memo
10 | const premiumNumbers = useMemo(() => {
11 | return ["0", "100", "200"];
12 | }, []);
13 |
14 | useEffect(() => {
15 | console.log("Premium numbers foi alterado!");
16 | }, [premiumNumbers]);
17 |
18 | return (
19 |
20 |
useMemo
21 |
setNumber(e.target.value)} />
22 | {premiumNumbers.includes(number) ?
Acertou o número!
: ""}
23 |
24 |
25 | );
26 | };
27 |
28 | export default HookUseMemo;
29 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/HookUseRef.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from "react";
2 |
3 | const HookUseRef = () => {
4 | // 1 - useRef básico
5 | const numberRef = useRef(0);
6 | const [counter, setCounter] = useState(0);
7 | const [counterB, setCounterB] = useState(0);
8 |
9 | useEffect(() => {
10 | numberRef.current = numberRef.current + 1;
11 | });
12 |
13 | // 2 - useRef e dom
14 | const inputRef = useRef();
15 | const [text, setText] = useState("");
16 |
17 | const handleSubmit = (e) => {
18 | e.preventDefault();
19 |
20 | setText("");
21 |
22 | inputRef.current.focus();
23 | };
24 |
25 | return (
26 |
27 |
useRef
28 | {/* 1 - useRef */}
29 |
O componente renderizou: {numberRef.current} vezes.
30 |
Counter 1: {counter}
31 |
32 |
Counter 2: {counterB}
33 |
34 | {/* 2 - useRef e DOM */}
35 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default HookUseRef;
50 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/HookUseState.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | const HookUseState = () => {
4 | // 1 - useState
5 | let userName = "João";
6 |
7 | const [name, setName] = useState("Matheus");
8 |
9 | const changeNames = () => {
10 | userName = "João Souza";
11 |
12 | setName("Matheus Battisti");
13 |
14 | console.log(userName);
15 | console.log(name);
16 | };
17 |
18 | // 2 - useState e input
19 | const [age, setAge] = useState(18);
20 |
21 | const handleSubmit = (e) => {
22 | e.preventDefault();
23 |
24 | console.log(age);
25 | };
26 |
27 | return (
28 |
29 | {/* 1 - useState */}
30 |
useState
31 |
Variável: {userName}
32 |
useState: {name}
33 |
34 | {/* 2 - useState e inputs */}
35 |
Digite sua idade:
36 |
44 |
Você tem {age} anos!
45 |
46 |
47 | );
48 | };
49 |
50 | export default HookUseState;
51 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/List.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 |
3 | const List = ({ getItems }) => {
4 | const [myItems, setMyItems] = useState([]);
5 |
6 | useEffect(() => {
7 | console.log("Buscando itens no DB...");
8 | setMyItems(getItems());
9 | }, [getItems]);
10 |
11 | return (
12 |
13 | {myItems.map((item) => (
14 |
{item}
15 | ))}
16 |
17 | );
18 | };
19 |
20 | export default List;
21 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/components/SomeComponent.js:
--------------------------------------------------------------------------------
1 | import { forwardRef, useRef, useImperativeHandle } from "react";
2 |
3 | const SomeComponent = forwardRef((props, ref) => {
4 | const localInputRef = useRef();
5 |
6 | useImperativeHandle(ref, () => {
7 | return {
8 | validate: () => {
9 | if (localInputRef.current.value.length > 3) {
10 | localInputRef.current.value = "";
11 | }
12 | },
13 | };
14 | });
15 |
16 | return (
17 |
18 |
Insira máximo 3 caracteres!
19 |
20 |
21 | );
22 | });
23 |
24 | export default SomeComponent;
25 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/hooks/usePrevious.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useDebugValue } from "react";
2 |
3 | export const usePrevious = (value) => {
4 | const ref = useRef;
5 |
6 | // useDebugValue
7 | useDebugValue("-- CUSTOM HOOK COM USEDEBUGVALUE --");
8 | useDebugValue("O número anterior é:" + value);
9 |
10 | useEffect(() => {
11 | ref.current = value;
12 | });
13 |
14 | return ref.current;
15 | };
16 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | padding-bottom: 500px;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/pages/About.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | // useContext
4 | import { useContext } from "react";
5 | import { SomeContext } from "../components/HookUseContext";
6 |
7 | const About = () => {
8 | const { contextValue } = useContext(SomeContext);
9 |
10 | return (
11 |
12 |
About
13 |
Valor do contexto: {contextValue}
14 |
15 |
16 | );
17 | };
18 |
19 | export default About;
20 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import HookUseEffect from "../components/HookUseEffect";
2 | import HookUseReducer from "../components/HookUseReducer";
3 | import HookUseState from "../components/HookUseState";
4 | import HookUseRef from "../components/HookUseRef";
5 | import HookUseCallback from "../components/HookUseCallback";
6 | import HookUseMemo from "../components/HookUseMemo";
7 | import HookUseEffectLayout from "../components/HookUseEffectLayout";
8 | import HookUseImperativeHandle from "../components/HookUseImperativeHandle";
9 | import HookCustom from "../components/HookCustom";
10 |
11 | // useContext
12 | import { useContext } from "react";
13 | import { SomeContext } from "../components/HookUseContext";
14 |
15 | const Home = () => {
16 | const { contextValue } = useContext(SomeContext);
17 |
18 | return (
19 |
20 |
21 |
22 |
23 |
useContext
24 |
Valor do contexto: {contextValue}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default Home;
37 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/11_REACT_HOOKS/reacthooks/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/.env:
--------------------------------------------------------------------------------
1 | PORT=5000
2 | DB_USER=reactgramref
3 | DB_PASS=BR4rtdDvN2z6Ipzq
4 | JWT_SECRET=thisioursecret
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | uploads/photos/*.png
3 | uploads/photos/*.jpg
4 | uploads/users/*.png
5 | uploads/users/*.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/app.js:
--------------------------------------------------------------------------------
1 | require("dotenv").config();
2 |
3 | const express = require("express");
4 | const path = require("path");
5 | const cors = require("cors");
6 |
7 | const port = process.env.PORT;
8 |
9 | const app = express();
10 |
11 | // Config JSON and form data response
12 | app.use(express.json());
13 | app.use(express.urlencoded({ extended: false }));
14 |
15 | // Solve CORS
16 | app.use(cors({ credentials: true, origin: "http://localhost:3000" }));
17 |
18 | // Upload directory
19 | app.use("/uploads", express.static(path.join(__dirname, "/uploads")));
20 |
21 | // db connection
22 | require("./config/db.js");
23 |
24 | // test route
25 | app.get("/", (req, res) => {
26 | res.send("API Working!");
27 | });
28 |
29 | // routes
30 | const router = require("./routes/Router.js");
31 |
32 | app.use(router);
33 |
34 | app.listen(port, () => {
35 | console.log(`App rodando na porta ${port}`);
36 | });
37 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/config/db.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const dbUser = process.env.DB_USER;
3 | const dbPassword = process.env.DB_PASS;
4 |
5 | const conn = async () => {
6 | try {
7 | const dbConn = await mongoose.connect(
8 | `mongodb+srv://${dbUser}:${dbPassword}@cluster0.ljrag.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`
9 | );
10 | console.log("Conectou ao banco de dados!");
11 |
12 | return dbConn;
13 | } catch (error) {
14 | console.log(error);
15 | }
16 | };
17 |
18 | conn();
19 |
20 | module.exports = conn;
21 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/middlewares/authGuard.js:
--------------------------------------------------------------------------------
1 | const User = require("../models/User");
2 |
3 | const jwt = require("jsonwebtoken");
4 |
5 | const jwtSecret = process.env.JWT_SECRET;
6 |
7 | const authGuard = async (req, res, next) => {
8 | const authHeader = req.headers["authorization"];
9 | const token = authHeader && authHeader.split(" ")[1];
10 |
11 | // Check if header has a token
12 | if (!token) return res.status(401).json({ errors: ["Acesso negado!"] });
13 |
14 | // Check if token is valid
15 | try {
16 | const verified = jwt.verify(token, jwtSecret);
17 |
18 | req.user = await User.findById(verified.id).select("-password");
19 |
20 | next();
21 | } catch (err) {
22 | res.status(400).json({ errors: ["O Token é inválido!"] });
23 | }
24 | };
25 |
26 | module.exports = authGuard;
27 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/middlewares/handleValidations.js:
--------------------------------------------------------------------------------
1 | const { validationResult } = require("express-validator");
2 |
3 | const validate = (req, res, next) => {
4 | const errors = validationResult(req);
5 |
6 | if (errors.isEmpty()) {
7 | return next();
8 | }
9 |
10 | const extractedErrors = [];
11 |
12 | errors.array().map((err) => extractedErrors.push(err.msg));
13 |
14 | return res.status(422).json({
15 | errors: extractedErrors,
16 | });
17 | };
18 |
19 | module.exports = validate;
20 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/middlewares/imageUpload.js:
--------------------------------------------------------------------------------
1 | const multer = require("multer");
2 | const path = require("path");
3 |
4 | // Destination to store image
5 | const imageStorage = multer.diskStorage({
6 | destination: function (req, file, cb) {
7 | let folder = "";
8 |
9 | if (req.baseUrl.includes("users")) {
10 | folder = "users";
11 | } else if (req.baseUrl.includes("photos")) {
12 | folder = "photos";
13 | }
14 | cb(null, `uploads/${folder}/`);
15 | },
16 | filename: (req, file, cb) => {
17 | cb(null, Date.now() + path.extname(file.originalname));
18 | },
19 | });
20 |
21 | const imageUpload = multer({
22 | storage: imageStorage,
23 | fileFilter(req, file, cb) {
24 | if (!file.originalname.match(/\.(png|jpg)$/)) {
25 | // upload only png and jpg format
26 | return cb(new Error("Por favor, envie apenas png ou jpg!"));
27 | }
28 | cb(undefined, true);
29 | },
30 | });
31 |
32 | module.exports = { imageUpload };
33 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/middlewares/photoValidations.js:
--------------------------------------------------------------------------------
1 | const { body } = require("express-validator");
2 |
3 | const photoInsertValidation = () => {
4 | return [
5 | body("title")
6 | .not()
7 | .equals("undefined")
8 | .withMessage("O título é obrigatório")
9 | .isString()
10 | .withMessage("O título é obrigatório")
11 | .isLength({ min: 3 })
12 | .withMessage("O nome precisa ter no mínimo 3 caracteres."),
13 | body("image").custom((value, { req }) => {
14 | if (!req.file) {
15 | throw new Error("A imagem é obrigatória");
16 | }
17 | return true;
18 | }),
19 | ];
20 | };
21 |
22 | const photoUpdateValidation = () => {
23 | return [
24 | body("image")
25 | .optional()
26 | .custom((value, { req }) => {
27 | if (!req.file) {
28 | throw new Error("A imagem é obrigatória");
29 | }
30 | return true;
31 | }),
32 | body("title")
33 | .optional()
34 | .isString()
35 | .withMessage("O título é obrigatório")
36 | .isLength({ min: 3 })
37 | .withMessage("O nome precisa ter no mínimo 3 caracteres."),
38 | ];
39 | };
40 |
41 | const commentValidation = () => {
42 | return [body("comment").isString().withMessage("O comentário é obrigatório")];
43 | };
44 |
45 | module.exports = {
46 | photoInsertValidation,
47 | photoUpdateValidation,
48 | commentValidation,
49 | };
50 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/middlewares/userValidations.js:
--------------------------------------------------------------------------------
1 | const { body } = require("express-validator");
2 |
3 | const userCreateValidation = () => {
4 | return [
5 | body("name")
6 | .isString()
7 | .withMessage("O nome é obrigatório.")
8 | .isLength({ min: 3 })
9 | .withMessage("O nome precisa ter no mínimo 3 caracteres."),
10 | body("email")
11 | .isString()
12 | .withMessage("O e-mail é obrigatório.")
13 | .isEmail()
14 | .withMessage("Insira um e-mail válido"),
15 | body("password")
16 | .isString()
17 | .withMessage("A senha é obrigatória.")
18 | .isLength({ min: 5 })
19 | .withMessage("A senha precisa de no mínimo 5 caracteres."),
20 | body("confirmPassword")
21 | .isString()
22 | .withMessage("A confirmação de senha é obrigatória.")
23 | .custom((value, { req }) => {
24 | if (value != req.body.password) {
25 | throw new Error("As senhas não são iguais.");
26 | }
27 | return true;
28 | }),
29 | ];
30 | };
31 |
32 | const loginValidation = () => {
33 | return [
34 | body("email")
35 | .isString()
36 | .withMessage("O e-mail é obrigatório.")
37 | .isEmail()
38 | .withMessage("Insira um e-mail válido"),
39 | body("password").isString().withMessage("A senha é obrigatória."),
40 | ];
41 | };
42 |
43 | const userUpdateValidation = () => {
44 | return [
45 | body("name")
46 | .optional()
47 | .isLength({ min: 3 })
48 | .withMessage("O nome precisa ter no mínimo 3 caracteres."),
49 | body("password")
50 | .optional()
51 | .isLength({ min: 5 })
52 | .withMessage("A senha precisa de no mínimo 5 caracteres."),
53 | ];
54 | };
55 |
56 | module.exports = {
57 | userCreateValidation,
58 | loginValidation,
59 | userUpdateValidation,
60 | };
61 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/models/Photo.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const { Schema } = mongoose;
3 |
4 | const photoSchema = new Schema(
5 | {
6 | image: String,
7 | title: String,
8 | likes: Array,
9 | comments: Array,
10 | userId: mongoose.ObjectId,
11 | userName: String,
12 | },
13 | {
14 | timestamps: true,
15 | }
16 | );
17 |
18 | Photo = mongoose.model("Photo", photoSchema);
19 |
20 | module.exports = Photo;
21 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/models/User.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const { Schema } = mongoose;
3 |
4 | const userSchema = new Schema(
5 | {
6 | name: String,
7 | email: String,
8 | password: String,
9 | profileImage: String,
10 | bio: String,
11 | },
12 | {
13 | timestamps: true,
14 | }
15 | );
16 |
17 | User = mongoose.model("User", userSchema);
18 |
19 | module.exports = User;
20 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactgram_backend",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "server": "nodemon ./app.js"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "nodemon": "^2.0.15"
15 | },
16 | "dependencies": {
17 | "bcryptjs": "^2.4.3",
18 | "cors": "^2.8.5",
19 | "dotenv": "^16.0.0",
20 | "express": "^4.17.3",
21 | "express-validator": "^6.14.0",
22 | "jsonwebtoken": "^8.5.1",
23 | "mongoose": "^6.2.9",
24 | "multer": "^1.4.4"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/routes/PhotoRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 |
4 | // Controller
5 | const {
6 | insertPhoto,
7 | deletePhoto,
8 | getAllPhotos,
9 | getUserPhotos,
10 | getPhotoById,
11 | updatePhoto,
12 | likePhoto,
13 | commentPhoto,
14 | searchPhotos,
15 | } = require("../controllers/PhotoController");
16 |
17 | // Middlewares
18 | const authGuard = require("../middlewares/authGuard");
19 | const validate = require("../middlewares/handleValidations");
20 | const {
21 | photoInsertValidation,
22 | photoUpdateValidation,
23 | commentValidation,
24 | } = require("../middlewares/photoValidations");
25 | const { imageUpload } = require("../middlewares/imageUpload");
26 |
27 | // Routes
28 | router.post(
29 | "/",
30 | authGuard,
31 | imageUpload.single("image"),
32 | photoInsertValidation(),
33 | validate,
34 | insertPhoto
35 | );
36 | router.delete("/:id", authGuard, deletePhoto);
37 | router.get("/", getAllPhotos);
38 | router.get("/user/:id", getUserPhotos);
39 | router.get("/search", searchPhotos);
40 |
41 | router.get("/:id", getPhotoById);
42 | router.put(
43 | "/:id",
44 | authGuard,
45 | imageUpload.single("image"),
46 | photoUpdateValidation(),
47 | validate,
48 | updatePhoto
49 | );
50 | router.put("/like/:id", authGuard, likePhoto);
51 | router.put(
52 | "/comment/:id",
53 | authGuard,
54 | commentValidation(),
55 | validate,
56 | commentPhoto
57 | );
58 |
59 | module.exports = router;
60 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/routes/Router.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express();
3 |
4 | router.use("/api/users", require("./UserRoutes"));
5 | router.use("/api/photos", require("./PhotoRoutes"));
6 |
7 | module.exports = router;
8 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/routes/UserRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 |
4 | // Controller
5 | const {
6 | register,
7 | getCurrentUser,
8 | login,
9 | update,
10 | getUserById,
11 | } = require("../controllers/UserController");
12 |
13 | // Middlewares
14 | const validate = require("../middlewares/handleValidations");
15 | const {
16 | userCreateValidation,
17 | loginValidation,
18 | userUpdateValidation,
19 | } = require("../middlewares/userValidations");
20 | const authGuard = require("../middlewares/authGuard");
21 | const { imageUpload } = require("../middlewares/imageUpload");
22 |
23 | // Routes
24 | router.post("/register", userCreateValidation(), validate, register);
25 | router.get("/profile", authGuard, getCurrentUser);
26 | router.post("/login", loginValidation(), validate, login);
27 | router.put(
28 | "/",
29 | authGuard,
30 | userUpdateValidation(),
31 | validate,
32 | imageUpload.single("profileImage"),
33 | update
34 | );
35 | router.get("/:id", getUserById);
36 |
37 | module.exports = router;
38 |
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/uploads/photos/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/backend/uploads/photos/.gitkeep
--------------------------------------------------------------------------------
/12_REACTGRAM/backend/uploads/users/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/backend/uploads/users/.gitkeep
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactgram_frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reduxjs/toolkit": "^1.8.1",
7 | "@testing-library/jest-dom": "^5.16.3",
8 | "@testing-library/react": "^12.1.4",
9 | "@testing-library/user-event": "^13.5.0",
10 | "react": "^17.0.2",
11 | "react-dom": "^17.0.2",
12 | "react-icons": "^4.3.1",
13 | "react-redux": "^7.2.7",
14 | "react-router-dom": "^6.2.2",
15 | "react-scripts": "5.0.0",
16 | "web-vitals": "^2.1.4"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": [
26 | "react-app",
27 | "react-app/jest"
28 | ]
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/frontend/public/logo192.png
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/frontend/public/logo512.png
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/App.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 70vh;
3 | }
4 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/components/Footer.css:
--------------------------------------------------------------------------------
1 | #footer {
2 | background-color: #121212;
3 | height: 200px;
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | border-top: 1px solid #363636;
8 | }
9 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import "./Footer.css";
2 |
3 | const Footer = () => {
4 | return (
5 |
8 | );
9 | };
10 |
11 | export default Footer;
12 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/components/LikeContainer.css:
--------------------------------------------------------------------------------
1 | .like {
2 | display: flex;
3 | align-items: center;
4 | border-top: 1px solid #363636;
5 | border-bottom: 1px solid #363636;
6 | }
7 |
8 | #home .like {
9 | border: none;
10 | }
11 |
12 | .like svg {
13 | font-size: 1.5em;
14 | cursor: pointer;
15 | }
16 |
17 | .like p {
18 | margin-left: 1em;
19 | }
20 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/components/LikeContainer.js:
--------------------------------------------------------------------------------
1 | import "./LikeContainer.css";
2 |
3 | import { BsHeart, BsHeartFill } from "react-icons/bs";
4 |
5 | const LikeContainer = ({ photo, user, handleLike }) => {
6 | return (
7 |
8 | {photo.likes && user && (
9 | <>
10 | {photo.likes.includes(user._id) ? (
11 |
12 | ) : (
13 |
handleLike(photo)} />
14 | )}
15 | {photo.likes.length} like(s)
16 | >
17 | )}
18 |
19 | );
20 | };
21 |
22 | export default LikeContainer;
23 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/components/Message.css:
--------------------------------------------------------------------------------
1 | .message {
2 | border-radius: 5px;
3 | padding: 5px 10px;
4 | position: relative;
5 | margin: 0;
6 | display: flex;
7 | justify-content: center;
8 | align-items: center;
9 | }
10 |
11 | .error {
12 |
13 | color: #721c24;
14 | background-color: #f8d7da;
15 | border-color: #f5c6cb;
16 | }
17 |
18 | .success {
19 | color: #155724;
20 | background-color: #d4edda;
21 | border-color: #c3e6cb;
22 | }
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/components/Message.js:
--------------------------------------------------------------------------------
1 | import "./Message.css";
2 |
3 | const Message = ({ msg, type }) => {
4 | return (
5 |
8 | );
9 | };
10 |
11 | export default Message;
12 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/components/Navbar.css:
--------------------------------------------------------------------------------
1 | #nav {
2 | display: flex;
3 | justify-content: space-between;
4 | align-items: center;
5 | background-color: #000;
6 | border-bottom: 1px solid #363636;
7 | padding: 0.1em 1em;
8 | }
9 |
10 | #search-form {
11 | position: relative;
12 | width: 20%;
13 | }
14 |
15 | #search-form svg {
16 | position: absolute;
17 | top: 10px;
18 | left: 9px;
19 | }
20 |
21 | #search-form input {
22 | padding-left: 2.5em;
23 | border: none;
24 | border-radius: 5px;
25 | width: 100%;
26 | margin: 0;
27 | }
28 |
29 | #nav-links {
30 | display: flex;
31 | align-items: center;
32 | }
33 |
34 | #nav-links li {
35 | margin-right: 1em;
36 | }
37 |
38 | #nav-links span {
39 | cursor: pointer;
40 | }
41 |
42 | #nav-links svg {
43 | font-size: 1.5em;
44 | }
45 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/components/PhotoItem.css:
--------------------------------------------------------------------------------
1 | .photo-item img {
2 | width: 100%;
3 | }
4 |
5 | #home .photo-item h2 {
6 | margin-bottom: 0.2em;
7 | }
8 |
9 | .photo-author {
10 | text-align: left;
11 | }
12 |
13 | .photo-author a {
14 | font-weight: bold;
15 | }
16 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/components/PhotoItem.js:
--------------------------------------------------------------------------------
1 | import "./PhotoItem.css";
2 |
3 | import { uploads } from "../utils/config";
4 |
5 | import { Link } from "react-router-dom";
6 |
7 | const PhotoItem = ({ photo }) => {
8 | return (
9 |
10 | {photo.image && (
11 |

12 | )}
13 |
{photo.title}
14 |
15 | Publicada por:{" "}
16 | {photo.userName}
17 |
18 |
19 | );
20 | };
21 |
22 | export default PhotoItem;
23 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/hooks/useAuth.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { useSelector } from "react-redux";
3 |
4 | export const useAuth = () => {
5 | const { user } = useSelector((state) => state.auth);
6 |
7 | const [auth, setAuth] = useState(false);
8 | const [loading, setLoading] = useState(true);
9 |
10 | useEffect(() => {
11 | if (user) {
12 | setAuth(true);
13 | } else {
14 | setAuth(false);
15 | }
16 |
17 | setLoading(false);
18 | }, [user]);
19 |
20 | return { auth, loading };
21 | };
22 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/hooks/useQuery.js:
--------------------------------------------------------------------------------
1 | import { useLocation } from "react-router-dom";
2 | import { useMemo } from "react";
3 |
4 | export function useQuery() {
5 | const { search } = useLocation();
6 |
7 | return useMemo(() => new URLSearchParams(search), [search]);
8 | }
9 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/hooks/useResetComponentMessage.js:
--------------------------------------------------------------------------------
1 | // Redux
2 | import { resetMessage } from "../slices/photoSlice";
3 |
4 | export const useResetComponentMessage = (dispatch) => {
5 | return () => {
6 | setTimeout(() => {
7 | dispatch(resetMessage());
8 | }, 2000);
9 | };
10 | };
11 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Roboto", sans-serif;
3 | margin: 0;
4 | padding: 0;
5 | box-sizing: border-box;
6 | background-color: #121212;
7 | color: #fafafa;
8 | }
9 |
10 | a {
11 | color: #fafafa;
12 | text-decoration: none;
13 | }
14 |
15 | .active {
16 | font-weight: bold;
17 | }
18 |
19 | ul {
20 | list-style: none;
21 | }
22 |
23 | form {
24 | display: flex;
25 | flex-direction: column;
26 | }
27 |
28 | label {
29 | display: flex;
30 | flex-direction: column;
31 | text-align: left;
32 | }
33 |
34 | label span {
35 | font-weight: bold;
36 | margin-bottom: 0.5em;
37 | color: #aaa;
38 | font-size: 0.7em;
39 | }
40 |
41 | input {
42 | background-color: #3b3b3b;
43 | color: #aaa;
44 | border: 1px solid #555;
45 | border-radius: 2px;
46 | padding: 10px 8px;
47 | outline: none;
48 | margin-bottom: 0.6em;
49 | }
50 |
51 | input::placeholder {
52 | color: #aaa;
53 | }
54 |
55 | .btn,
56 | button,
57 | input[type="submit"] {
58 | background-color: #0094f6;
59 | color: #fff;
60 | border: none;
61 | border-radius: 4px;
62 | padding: 10px 8px;
63 | font-weight: bold;
64 | cursor: pointer;
65 | opacity: 0.8;
66 | font-size: 1em;
67 | }
68 |
69 | input[type="submit"] {
70 | margin-top: 1em;
71 | }
72 |
73 | input:disabled {
74 | cursor: not-allowed;
75 | background-color: #000;
76 | }
77 |
78 | button:hover,
79 | input[type="submit"]:hover {
80 | opacity: 1;
81 | }
82 |
83 | .cancel-btn {
84 | background-color: #ccc;
85 | }
86 |
87 | .hide {
88 | display: none;
89 | }
90 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./App";
5 | import reportWebVitals from "./reportWebVitals";
6 |
7 | // Redux
8 | import { Provider } from "react-redux";
9 | import { store } from "./store";
10 |
11 | ReactDOM.render(
12 |
13 |
14 |
15 |
16 | ,
17 | document.getElementById("root")
18 | );
19 |
20 | // If you want to start measuring performance in your app, pass a function
21 | // to log results (for example: reportWebVitals(console.log))
22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
23 | reportWebVitals();
24 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/pages/Auth/Auth.css:
--------------------------------------------------------------------------------
1 | #login,
2 | #register {
3 | border: 1px solid #363636;
4 | background-color: #000;
5 | padding: 1.5em 2em;
6 | max-width: 33%;
7 | margin: 2em auto;
8 | }
9 |
10 | #login h2,
11 | #register h2 {
12 | text-align: center;
13 | font-size: 2.1em;
14 | margin-top: 0;
15 | }
16 |
17 | .subtitle {
18 | font-weight: bold;
19 | color: #999;
20 | margin-bottom: 1.5em;
21 | }
22 |
23 | #login form,
24 | #register form {
25 | padding-bottom: 1.5em;
26 | margin-bottom: 1.5em;
27 | border-bottom: 1px solid #363636;
28 | }
29 |
30 | #login p,
31 | #register p {
32 | text-align: center;
33 | }
34 |
35 | #login p a,
36 | #register p a {
37 | font-weight: bold;
38 | color: #0094f6;
39 | }
40 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/pages/Auth/Login.js:
--------------------------------------------------------------------------------
1 | import "./Auth.css";
2 |
3 | // Components
4 | import { Link } from "react-router-dom";
5 | import Message from "../../components/Message";
6 |
7 | // Hooks
8 | import { useEffect, useState } from "react";
9 | import { useSelector, useDispatch } from "react-redux";
10 |
11 | // Redux
12 | import { login, reset } from "../../slices/authSlice";
13 |
14 | const Login = () => {
15 | const [email, setEmail] = useState("");
16 | const [password, setPassword] = useState("");
17 |
18 | const dispatch = useDispatch();
19 |
20 | const { loading, error } = useSelector((state) => state.auth);
21 |
22 | const handleSubmit = (e) => {
23 | e.preventDefault();
24 |
25 | const user = {
26 | email,
27 | password,
28 | };
29 |
30 | console.log(user);
31 |
32 | dispatch(login(user));
33 | };
34 |
35 | // Clean all auth states
36 | useEffect(() => {
37 | dispatch(reset());
38 | }, [dispatch]);
39 |
40 | return (
41 |
42 |
ReactGram
43 |
Faça o login para ver o que há de novo.
44 |
61 |
62 | Não tem uma conta? Clique aqui
63 |
64 |
65 | );
66 | };
67 |
68 | export default Login;
69 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/pages/EditProfile/EditProfile.css:
--------------------------------------------------------------------------------
1 | #edit-profile {
2 | border: 1px solid #363636;
3 | background-color: #000;
4 | padding: 1.5em 2em;
5 | max-width: 40%;
6 | margin: 2em auto;
7 | text-align: center;
8 | }
9 |
10 | .subtitle {
11 | color: #ccc;
12 | }
13 |
14 | .profile-image {
15 | width: 150px;
16 | height: 150px;
17 | border-radius: 50%;
18 | margin-bottom: 1em;
19 | }
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/pages/Home/Home.css:
--------------------------------------------------------------------------------
1 | #home {
2 | width: 50%;
3 | margin: 0 auto;
4 | padding-top: 2em;
5 | }
6 |
7 | #home .btn {
8 | display: block;
9 | max-width: 80px;
10 | text-align: center;
11 | margin: 0.5em 0 2em 0;
12 | }
13 |
14 | .no-photos {
15 | text-align: center;
16 | }
17 |
18 | .no-photos a {
19 | color: #0094f6;
20 | }
21 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/pages/Home/Home.js:
--------------------------------------------------------------------------------
1 | import "./Home.css";
2 |
3 | // components
4 | import LikeContainer from "../../components/LikeContainer";
5 | import PhotoItem from "../../components/PhotoItem";
6 | import { Link } from "react-router-dom";
7 |
8 | // hooks
9 | import { useEffect } from "react";
10 | import { useSelector, useDispatch } from "react-redux";
11 | import { useResetComponentMessage } from "../../hooks/useResetComponentMessage";
12 |
13 | // Redux
14 | import { getPhotos, like } from "../../slices/photoSlice";
15 |
16 | const Home = () => {
17 | const dispatch = useDispatch();
18 |
19 | const resetMessage = useResetComponentMessage(dispatch);
20 |
21 | const { user } = useSelector((state) => state.auth);
22 | const { photos, loading } = useSelector((state) => state.photo);
23 |
24 | // Load all photos
25 | useEffect(() => {
26 | dispatch(getPhotos());
27 | }, [dispatch]);
28 |
29 | const handleLike = (photo = null) => {
30 | dispatch(like(photo._id));
31 |
32 | resetMessage();
33 | };
34 |
35 | if (loading) {
36 | return Carregando...
;
37 | }
38 |
39 | return (
40 |
41 | {photos &&
42 | photos.map((photo) => (
43 |
44 |
45 |
46 |
47 | Ver mais
48 |
49 |
50 | ))}
51 | {photos && photos.length === 0 && (
52 |
53 | Ainda não há fotos publicadas,{" "}
54 | clique aqui para começar.
55 |
56 | )}
57 |
58 | );
59 | };
60 |
61 | export default Home;
62 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/pages/Photo/Photo.css:
--------------------------------------------------------------------------------
1 | #photo {
2 | width: 50%;
3 | margin: 0 auto;
4 | text-align: center;
5 | margin-top: 2em;
6 | }
7 |
8 | #photo img {
9 | width: 100%;
10 | }
11 |
12 | .message-container {
13 | margin: 1em 0;
14 | }
15 |
16 | .comments {
17 | text-align: left;
18 | }
19 |
20 | .comments form {
21 | margin-bottom: 2em;
22 | padding-bottom: 1em;
23 | border-bottom: 1px solid #363636;
24 | }
25 |
26 | .author {
27 | display: flex;
28 | font-weight: bold;
29 | }
30 |
31 | #photo .author img {
32 | width: 50px;
33 | height: 50px;
34 | border-radius: 50%;
35 | margin-right: 1em;
36 | }
37 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/pages/Profile/Profile.css:
--------------------------------------------------------------------------------
1 | #profile {
2 | width: 50%;
3 | margin: 0 auto;
4 | }
5 |
6 | .profile-header {
7 | display: flex;
8 | align-items: center;
9 | flex-wrap: wrap;
10 | padding: 1em;
11 | border-bottom: 1px solid #363636;
12 | }
13 |
14 | .profile-header img {
15 | width: 100px;
16 | height: 100px;
17 | border-radius: 50%;
18 | margin-right: 2em;
19 | }
20 |
21 | .new-photo {
22 | padding: 1em;
23 | border-bottom: 1px solid #363636;
24 | }
25 |
26 | .photos-container {
27 | display: flex;
28 | flex-wrap: wrap;
29 | }
30 |
31 | .photo {
32 | width: 32%;
33 | margin: 0.3%;
34 | }
35 |
36 | .photo img {
37 | width: 100%;
38 | }
39 |
40 | .actions {
41 | display: flex;
42 | justify-content: space-around;
43 | padding: 10px;
44 | }
45 |
46 | .actions svg {
47 | cursor: pointer;
48 | }
49 |
50 | .edit-photo {
51 | margin-bottom: 1em;
52 | }
53 |
54 | .edit-photo img {
55 | width: 100%;
56 | margin-bottom: 1em;
57 | }
58 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/pages/Search/Search.css:
--------------------------------------------------------------------------------
1 | #search {
2 | width: 50%;
3 | margin: 0 auto;
4 | padding-top: 2em;
5 | }
6 |
7 | #search h2 {
8 | text-align: center;
9 | }
10 |
11 | #search .btn {
12 | display: block;
13 | max-width: 80px;
14 | text-align: center;
15 | margin: 0.5em 0 2em 0;
16 | }
17 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/pages/Search/Search.js:
--------------------------------------------------------------------------------
1 | import "./Search.css";
2 |
3 | // hooks
4 | import { useQuery } from "../../hooks/useQuery";
5 | import { useEffect } from "react";
6 | import { useSelector, useDispatch } from "react-redux";
7 | import { useResetComponentMessage } from "../../hooks/useResetComponentMessage";
8 |
9 | // components
10 | import LikeContainer from "../../components/LikeContainer";
11 | import PhotoItem from "../../components/PhotoItem";
12 | import { Link } from "react-router-dom";
13 |
14 | // Redux
15 | import { searchPhotos, like } from "../../slices/photoSlice";
16 |
17 | const Search = () => {
18 | const query = useQuery();
19 | const search = query.get("q");
20 |
21 | const dispatch = useDispatch();
22 |
23 | const resetMessage = useResetComponentMessage(dispatch);
24 |
25 | const { user } = useSelector((state) => state.auth);
26 | const { photos, loading } = useSelector((state) => state.photo);
27 |
28 | // Load all photos
29 | useEffect(() => {
30 | dispatch(searchPhotos(search));
31 | }, [dispatch, search]);
32 |
33 | const handleLike = (photo = null) => {
34 | dispatch(like(photo._id));
35 |
36 | resetMessage();
37 | };
38 |
39 | if (loading) {
40 | return Carregando...
;
41 | }
42 |
43 | return (
44 |
45 |
Você está buscando por: {search}
46 | {photos &&
47 | photos.map((photo) => (
48 |
49 |
50 |
51 |
52 | Ver mais
53 |
54 |
55 | ))}
56 |
57 | );
58 | };
59 |
60 | export default Search;
61 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/services/authService.js:
--------------------------------------------------------------------------------
1 | import { api, requestConfig } from "../utils/config";
2 |
3 | // Register a user
4 | const register = async (data) => {
5 | const config = requestConfig("POST", data);
6 |
7 | try {
8 | const res = await fetch(api + "/users/register", config)
9 | .then((res) => res.json())
10 | .catch((err) => err);
11 |
12 | if (res) {
13 | localStorage.setItem("user", JSON.stringify(res));
14 | }
15 |
16 | return res;
17 | } catch (error) {
18 | console.log(error);
19 | }
20 | };
21 |
22 | // Logout a user
23 | const logout = () => {
24 | localStorage.removeItem("user");
25 | };
26 |
27 | // Sign in a user
28 | const login = async (data) => {
29 | const config = requestConfig("POST", data);
30 |
31 | try {
32 | const res = await fetch(api + "/users/login", config)
33 | .then((res) => res.json())
34 | .catch((err) => err);
35 |
36 | if (res) {
37 | localStorage.setItem("user", JSON.stringify(res));
38 | }
39 |
40 | return res;
41 | } catch (error) {
42 | console.log(error);
43 | }
44 | };
45 |
46 | const authService = {
47 | register,
48 | logout,
49 | login,
50 | };
51 |
52 | export default authService;
53 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/services/userService.js:
--------------------------------------------------------------------------------
1 | import { api, requestConfig } from "../utils/config";
2 |
3 | // Get user details
4 | const profile = async (data, token) => {
5 | const config = requestConfig("GET", data, token);
6 |
7 | try {
8 | const res = await fetch(api + "/users/profile", config)
9 | .then((res) => res.json())
10 | .catch((err) => err);
11 |
12 | return res;
13 | } catch (error) {
14 | console.log(error);
15 | }
16 | };
17 |
18 | // Update user details
19 | const updateProfile = async (data, token) => {
20 | const config = requestConfig("PUT", data, token, true);
21 |
22 | try {
23 | const res = await fetch(api + "/users/", config)
24 | .then((res) => res.json())
25 | .catch((err) => err);
26 |
27 | return res;
28 | } catch (error) {
29 | console.log(error);
30 | }
31 | };
32 |
33 | // Get user details
34 | const getUserDetails = async (id) => {
35 | const config = requestConfig("GET");
36 |
37 | try {
38 | const res = await fetch(api + "/users/" + id, config)
39 | .then((res) => res.json())
40 | .catch((err) => err);
41 |
42 | return res;
43 | } catch (error) {
44 | console.log(error);
45 | }
46 | };
47 |
48 | const userService = {
49 | profile,
50 | updateProfile,
51 | getUserDetails,
52 | };
53 |
54 | export default userService;
55 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import authReducer from "./slices/authSlice";
3 | import userReducer from "./slices/userSlice";
4 | import photoReducer from "./slices/photoSlice";
5 |
6 | export const store = configureStore({
7 | reducer: {
8 | auth: authReducer,
9 | user: userReducer,
10 | photo: photoReducer,
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/12_REACTGRAM/frontend/src/utils/config.js:
--------------------------------------------------------------------------------
1 | export const api = "http://localhost:5000/api";
2 | export const uploads = "http://localhost:5000/uploads";
3 |
4 | export const requestConfig = (method, data, token = null, image = null) => {
5 | let config;
6 |
7 | if (image) {
8 | config = {
9 | method: method,
10 | body: data,
11 | headers: {},
12 | };
13 | } else if (method === "DELETE" || data === null) {
14 | config = {
15 | method: method,
16 | headers: {},
17 | };
18 | } else {
19 | config = {
20 | method: method,
21 | body: JSON.stringify(data),
22 | headers: {
23 | "Content-Type": "application/json",
24 | },
25 | };
26 | }
27 |
28 | if (token) {
29 | config.headers.Authorization = `Bearer ${token}`;
30 | }
31 |
32 | return config;
33 | };
34 |
--------------------------------------------------------------------------------
/12_REACTGRAM/images/f1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f1.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/f2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f2.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/f3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f3.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/f4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f4.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/f5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f5.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/f6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f6.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/f7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f7.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/f8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f8.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/f9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f9.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/u1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/u1.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/u2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/u2.jpg
--------------------------------------------------------------------------------
/12_REACTGRAM/images/u3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/u3.jpg
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/data/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "todos": [
3 | {
4 | "id": 0.7006336193587834,
5 | "title": "Nova tarefa ",
6 | "time": "2",
7 | "done": false
8 | },
9 | {
10 | "id": 0.7326144513928106,
11 | "title": "Outra tarefa",
12 | "time": "3",
13 | "done": true
14 | },
15 | {
16 | "id": 0.42808620877472925,
17 | "title": "Mais uma",
18 | "time": "4",
19 | "done": false
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "todo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.4",
7 | "@testing-library/react": "^12.1.4",
8 | "@testing-library/user-event": "^13.5.0",
9 | "json-server": "^0.17.0",
10 | "react": "^18.0.0",
11 | "react-dom": "^18.0.0",
12 | "react-icons": "^4.3.1",
13 | "react-scripts": "5.0.0",
14 | "web-vitals": "^2.1.4"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject",
21 | "server": "json-server --watch data/db.json --port 5000"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/13_CURSO_GRATUITO/todo/public/favicon.ico
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/13_CURSO_GRATUITO/todo/public/logo192.png
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/13_CURSO_GRATUITO/todo/public/logo512.png
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/src/index.css:
--------------------------------------------------------------------------------
1 | html {
2 | background: linear-gradient(
3 | 195deg,
4 | rgba(240, 98, 146, 1) 0%,
5 | rgba(221, 89, 133, 1) 98%
6 | );
7 | }
8 |
9 | body {
10 | margin: 0;
11 | padding: 0;
12 | font-family: Helvetica;
13 | }
14 |
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 | import reportWebVitals from "./reportWebVitals";
6 |
7 | const root = ReactDOM.createRoot(document.getElementById("root"));
8 |
9 | root.render(
10 |
11 |
12 |
13 | );
14 |
15 | // If you want to start measuring performance in your app, pass a function
16 | // to log results (for example: reportWebVitals(console.log))
17 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
18 | reportWebVitals();
19 |
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/13_CURSO_GRATUITO/todo/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fundamentos",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "5.0.0",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/2_FUNDAMENTOS/fundamentos/public/favicon.ico
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/2_FUNDAMENTOS/fundamentos/public/logo192.png
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/2_FUNDAMENTOS/fundamentos/public/logo512.png
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
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 | }
39 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/App.js:
--------------------------------------------------------------------------------
1 | import logo from "./logo.svg";
2 | import "./App.css";
3 |
4 | import FirstComponent from "./components/FirstComponent";
5 | import TemplateExpressions from "./components/TemplateExpressions";
6 | import MyComponent from "./components/MyComponent";
7 | import Events from "./components/Events";
8 | import Challenge from "./components/Challenge";
9 |
10 | function App() {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/components/Challenge.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Challenge = () => {
4 | const a = 10;
5 | const b = 15;
6 |
7 | return (
8 |
9 |
A: {a}
10 |
B: {b}
11 |
12 |
13 | );
14 | };
15 |
16 | export default Challenge;
17 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/components/Events.js:
--------------------------------------------------------------------------------
1 | const Events = () => {
2 | const handleMyEvent = (e) => {
3 | console.log(e);
4 | console.log("Ativou o evento!");
5 | };
6 |
7 | const renderSomething = (x) => {
8 | if (x) {
9 | return Renderizando isso!
;
10 | } else {
11 | return Também posso renderizar isso!
;
12 | }
13 | };
14 |
15 | return (
16 |
17 | {/* evento */}
18 |
19 |
20 |
21 | {/* lógica no evento */}
22 |
23 |
26 |
35 |
36 | {renderSomething(true)}
37 | {renderSomething(false)}
38 |
39 | );
40 | };
41 |
42 | export default Events;
43 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/components/FirstComponent.js:
--------------------------------------------------------------------------------
1 | import MyComponent from "./MyComponent";
2 |
3 | const FirstComponent = () => {
4 | // Um comentário
5 | return (
6 |
7 | {/* Um comentário no JSX */}
8 |
Título
9 |
Testando alguma classe
10 |
11 |
12 | );
13 | };
14 |
15 | export default FirstComponent;
16 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/components/MyComponent.js:
--------------------------------------------------------------------------------
1 | const MyComponent = () => {
2 | return (
3 |
4 |
Estou sendo reaproveitado em vários lugares!
5 |
6 | );
7 | };
8 |
9 | export default MyComponent;
10 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/components/TemplateExpressions.js:
--------------------------------------------------------------------------------
1 | const TemplateExpressions = () => {
2 | const name = "Matheus";
3 | const data = {
4 | age: 31,
5 | job: "Programmer",
6 | };
7 |
8 | return (
9 |
10 |
Olá {name}, tudo bem?
11 |
Você atua como: {data.job}
12 |
{4 + 4}
13 |
14 | );
15 | };
16 |
17 | export default TemplateExpressions;
18 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/2_FUNDAMENTOS/fundamentos/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "avancando",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "5.0.0",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/public/favicon.ico
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/public/img1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/public/img1.jpg
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/public/logo192.png
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/public/logo512.png
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
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 | }
39 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/assets/city.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/src/assets/city.jpg
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/CarDetails.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const CarDetails = ({ brand, km, color }) => {
4 | return (
5 |
6 |
Detalhes do carro:
7 |
8 | - Marca: {brand}
9 | - Kilometragem: {km}
10 | - Cor: {color}
11 |
12 |
13 | );
14 | };
15 |
16 | export default CarDetails;
17 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/ChangeMessageState.js:
--------------------------------------------------------------------------------
1 | const ChangeMessageState = ({ handleMessage }) => {
2 | const messages = ["Oi", "Olá", "Tudo bem?"];
3 |
4 | return (
5 |
6 |
7 |
8 |
9 |
10 | );
11 | };
12 |
13 | export default ChangeMessageState;
14 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/ConditionalRender.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const ConditionalRender = () => {
4 | const x = true;
5 |
6 | const name = "Matheus";
7 |
8 | return (
9 |
10 |
Isso será exibido?
11 | {x &&
Se x for true sim!
}
12 |
Render ternário:
13 | {name === "João" ? (
14 |
17 | ) : (
18 |
19 |
Nome não encontrado!
20 |
21 | )}
22 |
23 | );
24 | };
25 |
26 | export default ConditionalRender;
27 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/Container.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Container = ({ children }) => {
4 | return (
5 |
6 |
Conteúdo do componente pai:
7 | {children}
8 |
9 | );
10 | };
11 |
12 | export default Container;
13 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/ExecuteFunction.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const ExecuteFunction = ({ myFunction }) => {
4 | return (
5 |
6 |
7 |
8 | );
9 | };
10 |
11 | export default ExecuteFunction;
12 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/Fragment.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Fragment = () => {
4 | return (
5 | <>
6 |
7 |
Temos dois elementos pai
8 |
9 |
10 |
Este também é
11 |
12 | >
13 | );
14 | };
15 |
16 | export default Fragment;
17 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/ListRender.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | const ListRender = () => {
4 | const [list] = useState(["Matheus", "Pedro", "Josias"]);
5 | const [users, setUsers] = useState([
6 | { id: 1, name: "Matheus", age: 31 },
7 | { id: 2, name: "Jones", age: 19 },
8 | { id: 3, name: "Scorpion", age: 201 },
9 | ]);
10 |
11 | const deleteRandom = () => {
12 | const randomNumber = Math.floor(Math.random() * 4);
13 |
14 | setUsers((prevUsers) => {
15 | return prevUsers.filter((user) => randomNumber !== user.id);
16 | });
17 | };
18 |
19 | return (
20 |
21 | {/* render sem key primeiramente */}
22 |
23 | {list.map((item, i) => (
24 | - {item}
25 | ))}
26 |
27 |
28 | {users.map((user) => (
29 | -
30 | {user.name} - {user.age} anos
31 |
32 | ))}
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | export default ListRender;
40 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/ManageData.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | const ManageData = () => {
4 | const someData = 10;
5 |
6 | const [anotherNumber, setAnotherNumber] = useState(15);
7 |
8 | return (
9 |
10 |
11 |
Valor: {someData}
12 |
13 |
14 |
15 |
Valor: {anotherNumber}
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default ManageData;
23 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/MessageState.js:
--------------------------------------------------------------------------------
1 | const MessageState = ({ msg }) => {
2 | return (
3 |
4 |
A mensagem é: {msg}
5 |
6 | );
7 | };
8 |
9 | export default MessageState;
10 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/components/ShowUserName.js:
--------------------------------------------------------------------------------
1 | const ShowUserName = (props) => {
2 | return (
3 |
4 |
O nome do usuário é: {props.name}
5 |
6 | );
7 | };
8 |
9 | export default ShowUserName;
10 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 |
9 | padding-bottom: 500px;
10 | }
11 |
12 | code {
13 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
14 | monospace;
15 | }
16 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/3_AVANCANDO_NO_REACT/avancando/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "challengecss",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "5.0.0",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/challengecss/public/favicon.ico
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/challengecss/public/logo192.png
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/challengecss/public/logo512.png
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .car-container {
6 | display: flex;
7 | justify-content: center;
8 | }
9 |
10 | .car-container div {
11 | margin: 50px;
12 | }
13 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/src/App.js:
--------------------------------------------------------------------------------
1 | import logo from "./logo.svg";
2 | import "./App.css";
3 | import Car from "./components/Car";
4 |
5 | function App() {
6 | const myCars = [
7 | { name: "Fusca", km: 10000, color: "Branca" },
8 | { name: "Polo", km: 32000, color: "Cinza" },
9 | { name: "Onix", km: 0, color: "Preto" },
10 | ];
11 | return (
12 |
13 |
Showroom de carros
14 |
15 | {myCars.map((car) => (
16 |
17 | ))}
18 |
19 |
20 | );
21 | }
22 |
23 | export default App;
24 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/src/components/Car.js:
--------------------------------------------------------------------------------
1 | import styles from "./Car.module.css";
2 |
3 | const Car = ({ car }) => {
4 | return (
5 |
6 |
{car.name}
7 |
KM: {car.km}
8 |
Cor: {car.color}
9 |
10 | );
11 | };
12 |
13 | export default Car;
14 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/src/components/Car.module.css:
--------------------------------------------------------------------------------
1 | .card {
2 | border: 1px solid purple;
3 | border-radius: 10px;
4 | padding: 10px 50px;
5 | }
6 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 0;
3 | margin: 0;
4 | font-family: Verdana;
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/4_CSS_REACT/challengecss/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "css",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "5.0.0",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/css/public/favicon.ico
--------------------------------------------------------------------------------
/4_CSS_REACT/css/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/css/public/logo192.png
--------------------------------------------------------------------------------
/4_CSS_REACT/css/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/css/public/logo512.png
--------------------------------------------------------------------------------
/4_CSS_REACT/css/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
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 | }
39 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import MyComponent from "./components/MyComponent";
3 | import Title from "./components/Title";
4 |
5 | function App() {
6 | const n = 15;
7 |
8 | const redTitle = true;
9 |
10 | return (
11 |
12 | {/* Global css */}
13 |
React com CSS
14 | {/* Component css */}
15 |
Este parágrafo pegou estilo do componente!
16 |
17 |
18 | {/* Inline css */}
19 |
26 | Este elemento foi estilizado inline
27 |
28 | {/* Dinâmico css */}
29 |
10 ? { color: "purple" } : { color: "magenta" }}>
30 | CSS dinâmico
31 |
32 |
33 | CSS dinâmico 2
34 |
35 | {/* classe dinâmica */}
36 |
37 | Este título vai ter uma classe dinâmica
38 |
39 | {/* CSS modules */}
40 |
41 |
Este não recebe título do CSS modules
42 |
43 | );
44 | }
45 |
46 | export default App;
47 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/components/MyComponent.css:
--------------------------------------------------------------------------------
1 | p {
2 | color: green;
3 | border-bottom: 1px solid #000;
4 | }
5 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/components/MyComponent.js:
--------------------------------------------------------------------------------
1 | import "./MyComponent.css";
2 |
3 | const MyComponent = () => {
4 | return (
5 |
6 |
CSS de componente
7 |
Este parágrafo vai ter um estilo
8 |
9 | );
10 | };
11 |
12 | export default MyComponent;
13 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/components/Title.js:
--------------------------------------------------------------------------------
1 | import styles from "./Title.module.css";
2 |
3 | const Title = () => {
4 | return Meu título!
;
5 | };
6 |
7 | export default Title;
8 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/components/Title.module.css:
--------------------------------------------------------------------------------
1 | .title {
2 | color: orange;
3 | background-color: #000;
4 | padding: 10px;
5 | }
6 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: Helvetica;
5 | padding-bottom: 500px;
6 | }
7 |
8 | h1 {
9 | color: red;
10 | }
11 |
12 | /* dynamic class */
13 | .red-title {
14 | font-size: 50px;
15 | color: red;
16 | }
17 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/4_CSS_REACT/css/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "forms",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "5.0.0",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/5_FORM_EM_REACT/forms/public/favicon.ico
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/5_FORM_EM_REACT/forms/public/logo192.png
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/5_FORM_EM_REACT/forms/public/logo512.png
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
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 | }
39 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import MyForm from "./components/MyForm";
3 |
4 | function App() {
5 | return (
6 |
7 |
Forms
8 |
9 |
10 | );
11 | }
12 |
13 | export default App;
14 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/src/components/MyForm.css:
--------------------------------------------------------------------------------
1 | form {
2 | width: 500px;
3 | margin: 0 auto;
4 | text-align: left;
5 | }
6 |
7 | input,
8 | textarea,
9 | select {
10 | display: block;
11 | margin-top: 10px;
12 | }
13 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/5_FORM_EM_REACT/forms/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretword",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "5.0.0",
12 | "web-vitals": "^2.1.4"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/6_PROJETO_SECRET_WORD/secretword/public/favicon.ico
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/6_PROJETO_SECRET_WORD/secretword/public/logo192.png
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/6_PROJETO_SECRET_WORD/secretword/public/logo512.png
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/components/Game.css:
--------------------------------------------------------------------------------
1 | .game h1 {
2 | font-size: 2.5em;
3 | }
4 |
5 | .points span {
6 | font-weight: bold;
7 | }
8 |
9 | .tip span {
10 | color: #ecfa00;
11 | }
12 |
13 | .wordContainer {
14 | margin: 1.5em;
15 | padding: 1.5em;
16 | border: 20px solid #ecfa00;
17 | display: flex;
18 | }
19 |
20 | .letter,
21 | .blankSquare {
22 | font-size: 70px;
23 | line-height: 1.5;
24 | border: 3px solid #000;
25 | height: 100px;
26 | width: 100px;
27 | text-transform: uppercase;
28 | background-color: #fff;
29 | color: #000;
30 | font-weight: bold;
31 | }
32 |
33 | .letterContainer p {
34 | margin-bottom: 1.2em;
35 | }
36 |
37 | .letterContainer form {
38 | display: flex;
39 | align-items: center;
40 | justify-content: center;
41 | }
42 |
43 | .letterContainer input {
44 | height: 50px;
45 | width: 50px;
46 | font-size: 2em;
47 | text-align: center;
48 | margin-right: 1em;
49 | }
50 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/components/GameOver.css:
--------------------------------------------------------------------------------
1 | h2 span {
2 | color: #ecfa00;
3 | font-size: 1.5em;
4 | }
5 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/components/GameOver.js:
--------------------------------------------------------------------------------
1 | import "./GameOver.css";
2 |
3 | const GameOver = ({ retry, score }) => {
4 | return (
5 |
6 |
Fim de jogo!
7 |
8 | A sua pontuação foi: {score}!
9 |
10 |
11 |
12 | );
13 | };
14 |
15 | export default GameOver;
16 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/components/StartScreen.css:
--------------------------------------------------------------------------------
1 | .start h1 {
2 | font-size: 3.5em;
3 | }
4 |
5 | .start p {
6 | margin-bottom: 2em;
7 | color: #ecfa00;
8 | }
9 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/components/StartScreen.js:
--------------------------------------------------------------------------------
1 | import "./StartScreen.css";
2 |
3 | const GameStart = ({ startGame }) => {
4 | return (
5 |
6 |
Secret Word
7 |
Clique no botão abaixo para começar a jogar
8 |
9 |
10 | );
11 | };
12 |
13 | export default GameStart;
14 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/data/words.js:
--------------------------------------------------------------------------------
1 | export const wordsList = {
2 | carro: ["Motor", "Porta", "Capô", "Pneu", "Antena"],
3 | fruta: ["Banana", "Maçã", "Pêra", "Mamão", "Laranja"],
4 | corpo: ["Braço", "Perna", "Cérebro", "Pescoço", "Olhos"],
5 | computador: ["Mouse", "Teclado", "Monitor", "Gabinete"],
6 | programação: ["Linguagem", "Framework", "JavaScript", "React"],
7 | alimento: ["Arroz", "Feijão", "Carne", "Leite", "Ovo"],
8 | };
9 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/index.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | height: 100%;
4 | }
5 |
6 | body {
7 | font-family: Helvetica;
8 | margin: 0;
9 | padding: 0;
10 | background: rgb(9, 35, 175);
11 | background: linear-gradient(
12 | 180deg,
13 | rgba(9, 35, 175, 1) 0%,
14 | rgba(0, 0, 0, 1) 100%
15 | );
16 | color: #fff;
17 | }
18 |
19 | button {
20 | background-color: #1646a0;
21 | color: #fff;
22 | padding: 0 45px;
23 | border: 2px solid #fff;
24 | border-radius: 25px;
25 | height: 50px;
26 | text-transform: uppercase;
27 | font-weight: bold;
28 | font-size: 1.2em;
29 | cursor: pointer;
30 | transition: 0.4s;
31 | }
32 |
33 | button:hover {
34 | background: #0923af;
35 | }
36 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/6_PROJETO_SECRET_WORD/secretword/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/data/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "products": [
3 | {
4 | "id": 1,
5 | "name": "Camisa",
6 | "price": 59.9
7 | },
8 | {
9 | "id": 2,
10 | "name": "Calça vermelha",
11 | "price": 90
12 | },
13 | {
14 | "id": 3,
15 | "name": "Boné aba reta",
16 | "price": 29.9
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/data/db.json.backup:
--------------------------------------------------------------------------------
1 | {
2 | "products": [
3 | {
4 | "id": 1,
5 | "name": "Camisa",
6 | "price": 59.9
7 | },
8 | {
9 | "id": 2,
10 | "name": "Calça vermelha",
11 | "price": 90
12 | },
13 | {
14 | "id": 3,
15 | "name": "Boné aba reta",
16 | "price": 29.9
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "httpreact",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "json-server": "^0.17.0",
10 | "react": "^17.0.2",
11 | "react-dom": "^17.0.2",
12 | "react-scripts": "5.0.0",
13 | "web-vitals": "^2.1.4"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject",
20 | "server": "json-server --watch data/db.json"
21 | },
22 | "eslintConfig": {
23 | "extends": [
24 | "react-app",
25 | "react-app/jest"
26 | ]
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/7_REQ_HTTP_REACT/httpreact/public/favicon.ico
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/7_REQ_HTTP_REACT/httpreact/public/logo192.png
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/7_REQ_HTTP_REACT/httpreact/public/logo512.png
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
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 | }
39 |
40 | .add-product {
41 | border-top: 1px solid #000;
42 | }
43 |
44 | form {
45 | display: flex;
46 | flex-direction: column;
47 | align-items: center;
48 | justify-content: center;
49 | }
50 |
51 | form input {
52 | display: flex;
53 | flex-direction: column;
54 | margin-bottom: 15px;
55 | }
56 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/7_REQ_HTTP_REACT/httpreact/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/data/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "products": [
3 | {
4 | "id": 1,
5 | "name": "Camisa",
6 | "price": 59.9
7 | },
8 | {
9 | "id": 2,
10 | "name": "Calça vermelha",
11 | "price": 90
12 | },
13 | {
14 | "id": 3,
15 | "name": "Boné aba reta",
16 | "price": 29.9
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/data/db.json.backup:
--------------------------------------------------------------------------------
1 | {
2 | "products": [
3 | {
4 | "id": 1,
5 | "name": "Camisa",
6 | "price": 59.9
7 | },
8 | {
9 | "id": 2,
10 | "name": "Calça vermelha",
11 | "price": 90
12 | },
13 | {
14 | "id": 3,
15 | "name": "Boné aba reta",
16 | "price": 29.9
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactrouter",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.3",
8 | "@testing-library/user-event": "^13.5.0",
9 | "json-server": "^0.17.0",
10 | "react": "^17.0.2",
11 | "react-dom": "^17.0.2",
12 | "react-router-dom": "^6.2.1",
13 | "react-scripts": "5.0.0",
14 | "web-vitals": "^2.1.4"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject",
21 | "server": "json-server --watch data/db.json"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/8_REACT_ROUTER/reactrouter/public/favicon.ico
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/8_REACT_ROUTER/reactrouter/public/logo192.png
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/8_REACT_ROUTER/reactrouter/public/logo512.png
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
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 | }
39 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 |
3 | // 1 - config react router, sem links
4 | import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
5 |
6 | // pages
7 | import Home from "./pages/Home";
8 | import About from "./pages/About";
9 |
10 | // 2 - adicionando links
11 | // components
12 | import Navbar from "./components/Navbar";
13 | import Product from "./pages/Product";
14 | import Info from "./pages/Info";
15 | import NotFound from "./pages/NotFound";
16 | import Search from "./pages/Search";
17 | import { SearchForm } from "./components/SearchForm";
18 |
19 | function App() {
20 | return (
21 |
22 |
React Router
23 |
24 |
25 | {/* 9 - search */}
26 |
27 |
28 | } />
29 | } />
30 | {/* 6 - nested route */}
31 | } />
32 | {/* 4 - rota dinamica */}
33 | } />
34 |
35 | {/* 9 search params */}
36 | } />
37 | {/* 10 - redirect */}
38 | } />
39 | {/* 7 - no match route */}
40 | } />
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | export default App;
48 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/components/Navbar.css:
--------------------------------------------------------------------------------
1 | nav {
2 | display: flex;
3 | justify-content: center;
4 | }
5 |
6 | nav a {
7 | margin: 0 10px;
8 | padding: 5px;
9 | text-decoration: none;
10 | color: #000;
11 | }
12 |
13 | nav a:hover {
14 | color: #ccc;
15 | }
16 |
17 | .active {
18 | background-color: #000;
19 | color: #fff;
20 | }
21 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import { Link, NavLink } from "react-router-dom";
2 |
3 | import "./Navbar.css";
4 |
5 | const Navbar = () => {
6 | return (
7 |
24 | );
25 | };
26 |
27 | export default Navbar;
28 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/components/SearchForm.js:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 |
3 | import { useState } from "react";
4 |
5 | export const SearchForm = () => {
6 | const navigate = useNavigate();
7 | const [query, setQuery] = useState();
8 |
9 | const handleSubmit = (e) => {
10 | e.preventDefault();
11 |
12 | navigate("/search?q=" + query);
13 | };
14 | return (
15 |
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/pages/About.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const About = () => {
4 | return (
5 |
6 |
About
7 |
8 | );
9 | };
10 |
11 | export default About;
12 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/pages/Home.css:
--------------------------------------------------------------------------------
1 | .products {
2 | display: flex;
3 | justify-content: center;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .products li {
8 | border: 1px solid #efefef;
9 | border-radius: 5px;
10 | padding: 10px;
11 | text-align: center;
12 | list-style: none;
13 | margin: 0 10px;
14 | width: 25%;
15 | }
16 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 | import { useFetch } from "../hooks/useFetch";
3 |
4 | import "./Home.css";
5 |
6 | const Home = () => {
7 | // 3 - carregamento de dados
8 | const url = "http://localhost:3000/products";
9 |
10 | const { data: items, loading, error } = useFetch(url);
11 |
12 | return (
13 |
14 |
Produtos
15 | {loading &&
Carregando dados...
}
16 | {error &&
{error}
}
17 |
18 | {items &&
19 | items.map((product) => (
20 | -
21 |
{product.name}
22 | R$: {product.price}
23 | {/* 4 - rota dinamica */}
24 | Detalhes
25 |
26 | ))}
27 |
28 |
29 | );
30 | };
31 |
32 | export default Home;
33 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/pages/Info.js:
--------------------------------------------------------------------------------
1 | import { useParams } from "react-router-dom";
2 |
3 | const Info = () => {
4 | const { id } = useParams();
5 |
6 | return (
7 |
8 |
Informações sobre o produto: {id}
9 |
10 | );
11 | };
12 |
13 | export default Info;
14 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/pages/NotFound.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const NotFound = () => {
4 | return (
5 |
6 |
404
7 |
Não há nada aqui =(
8 |
9 | );
10 | };
11 |
12 | export default NotFound;
13 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/pages/Product.js:
--------------------------------------------------------------------------------
1 | import { useFetch } from "../hooks/useFetch";
2 |
3 | import { useParams, Link } from "react-router-dom";
4 |
5 | const Product = () => {
6 | //4 - rota dinamica
7 | const { id } = useParams();
8 |
9 | //5 - carregamento de dados
10 | const url = "http://localhost:3000/products/" + id;
11 |
12 | const { data: product, loading, error } = useFetch(url);
13 |
14 | return (
15 | <>
16 | ID do produto: {id}
17 |
18 | {error && Ocorreu um erro...
}
19 | {loading && Carregando produto...
}
20 | {product && (
21 |
22 |
{product.name}
23 |
R${product.price}
24 | {/* 6 - nested routes */}
25 |
Mais informações
26 |
27 | )}
28 | >
29 | );
30 | };
31 |
32 | export default Product;
33 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/pages/Search.js:
--------------------------------------------------------------------------------
1 | import { useSearchParams, Link } from "react-router-dom";
2 |
3 | import { useFetch } from "../hooks/useFetch";
4 |
5 | const Search = () => {
6 | let [searchParams] = useSearchParams();
7 |
8 | const url = "http://localhost:3000/products?" + searchParams;
9 |
10 | const { data: items, loading, error } = useFetch(url);
11 |
12 | return (
13 |
14 |
Resultados disponíveis:
15 |
16 | {items &&
17 | items.map((product) => (
18 | -
19 |
{product.name}
20 | R$: {product.price}
21 | Detalhes
22 |
23 | ))}
24 |
25 |
26 | );
27 | };
28 |
29 | export default Search;
30 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/8_REACT_ROUTER/reactrouter/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "context",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.3",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-router-dom": "^6.2.1",
12 | "react-scripts": "5.0.0",
13 | "web-vitals": "^2.1.4"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": [
23 | "react-app",
24 | "react-app/jest"
25 | ]
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/9_CONTEXT/context/public/favicon.ico
--------------------------------------------------------------------------------
/9_CONTEXT/context/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/9_CONTEXT/context/public/logo192.png
--------------------------------------------------------------------------------
/9_CONTEXT/context/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/9_CONTEXT/context/public/logo512.png
--------------------------------------------------------------------------------
/9_CONTEXT/context/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
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 | }
39 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/App.js:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 |
3 | // 1 - config react router, sem links
4 | import { BrowserRouter, Routes, Route } from "react-router-dom";
5 |
6 | // pages
7 | import Home from "./pages/Home";
8 | import About from "./pages/About";
9 | import Products from "./pages/Products";
10 |
11 | // 2 - adicionando links
12 | // components
13 | import Navbar from "./components/Navbar";
14 |
15 | function App() {
16 | return (
17 |
18 |
Context
19 |
20 |
21 |
22 | } />
23 | } />
24 | } />
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | export default App;
32 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/components/ChangeCounter.js:
--------------------------------------------------------------------------------
1 | // 3 - alterando valor do contador
2 | import { useContext } from "react";
3 |
4 | import { CounterContext } from "../context/CounterContext";
5 | import { useCounterContext } from "../hooks/useCounterContext";
6 |
7 | const ChangeCounter = () => {
8 | // const { counter, setCounter } = useContext(CounterContext);
9 | // 4 - refatorando para hook
10 | const { counter, setCounter } = useCounterContext();
11 |
12 | return (
13 |
14 |
17 |
18 | );
19 | };
20 |
21 | export default ChangeCounter;
22 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/components/Navbar.css:
--------------------------------------------------------------------------------
1 | nav {
2 | display: flex;
3 | justify-content: center;
4 | }
5 |
6 | nav a {
7 | margin: 0 10px;
8 | padding: 5px;
9 | text-decoration: none;
10 | color: #000;
11 | }
12 |
13 | nav a:hover {
14 | color: #ccc;
15 | }
16 |
17 | .active {
18 | background-color: #000;
19 | color: #fff;
20 | }
21 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import { NavLink } from "react-router-dom";
2 |
3 | import "./Navbar.css";
4 |
5 | const Navbar = () => {
6 | return (
7 |
27 | );
28 | };
29 |
30 | export default Navbar;
31 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/context/CounterContext.js:
--------------------------------------------------------------------------------
1 | // 1 - criar contexto
2 | import { createContext, useState } from "react";
3 |
4 | export const CounterContext = createContext();
5 |
6 | // 2 - criar provider
7 | export const CounterContextProvider = ({ children }) => {
8 | const [counter, setCounter] = useState(0);
9 |
10 | return (
11 |
12 | {children}
13 |
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/context/TitleColorContext.js:
--------------------------------------------------------------------------------
1 | // contexto mais complexo
2 | import { createContext, useReducer, useState } from "react";
3 |
4 | export const TitleColorContext = createContext();
5 |
6 | export const titleColorReducer = (state, action) => {
7 | switch (action.type) {
8 | case "RED":
9 | return { ...state, color: "red" };
10 | case "BLUE":
11 | return { ...state, color: "blue" };
12 | default:
13 | return state;
14 | }
15 | };
16 |
17 | export const TitleColorContextProvider = ({ children }) => {
18 | const [state, dispatch] = useReducer(titleColorReducer, {
19 | color: "purple",
20 | });
21 |
22 | console.log("Title Color Context:", state);
23 |
24 | return (
25 |
26 | {children}
27 |
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/hooks/useCounterContext.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { CounterContext } from "../context/CounterContext";
3 |
4 | export const useCounterContext = () => {
5 | const context = useContext(CounterContext);
6 |
7 | if (!context) {
8 | console.log("Contexto não encontrado.");
9 | }
10 |
11 | return context;
12 | };
13 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/hooks/useTitleColorContext.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { TitleColorContext } from "../context/TitleColorContext";
3 |
4 | export const useTitleColorContext = () => {
5 | const context = useContext(TitleColorContext);
6 |
7 | if (!context) {
8 | console.log("Contexto não encontrado.");
9 | }
10 |
11 | return context;
12 | };
13 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./App";
5 | import reportWebVitals from "./reportWebVitals";
6 | import { CounterContextProvider } from "./context/CounterContext";
7 | import { TitleColorContextProvider } from "./context/TitleColorContext";
8 |
9 | ReactDOM.render(
10 |
11 | {/* 2 - criando provider */}
12 |
13 |
14 |
15 |
16 |
17 | ,
18 | document.getElementById("root")
19 | );
20 |
21 | // If you want to start measuring performance in your app, pass a function
22 | // to log results (for example: reportWebVitals(console.log))
23 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
24 | reportWebVitals();
25 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/pages/About.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 |
3 | import { CounterContext } from "../context/CounterContext";
4 |
5 | import { useTitleColorContext } from "../hooks/useTitleColorContext";
6 |
7 | const About = () => {
8 | const { counter } = useContext(CounterContext);
9 |
10 | // 5 - contexto mais complexo
11 | const { color } = useTitleColorContext();
12 |
13 | return (
14 |
15 |
About
16 |
Valor contador: {counter}
17 |
18 | );
19 | };
20 |
21 | export default About;
22 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/pages/Home.css:
--------------------------------------------------------------------------------
1 | .products {
2 | display: flex;
3 | justify-content: center;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .products li {
8 | border: 1px solid #efefef;
9 | border-radius: 5px;
10 | padding: 10px;
11 | text-align: center;
12 | list-style: none;
13 | margin: 0 10px;
14 | width: 25%;
15 | }
16 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import "./Home.css";
2 |
3 | import { useContext } from "react";
4 |
5 | import { CounterContext } from "../context/CounterContext";
6 | import ChangeCounter from "../components/ChangeCounter";
7 |
8 | // 4 - refatorando com hook
9 | import { useCounterContext } from "../hooks/useCounterContext";
10 | import { useTitleColorContext } from "../hooks/useTitleColorContext";
11 |
12 | const Home = () => {
13 | // 2 - criar provider
14 | // const { counter } = useContext(CounterContext);
15 | const { counter } = useCounterContext();
16 |
17 | // 5 - contexto mais complexo
18 | const { color, dispatch } = useTitleColorContext();
19 |
20 | // 6 - alterando contexto complexo
21 | const setTitleColor = (color) => {
22 | dispatch({ type: color });
23 | };
24 |
25 | return (
26 |
27 |
Home
28 |
Valor contador: {counter}
29 | {/* 3 - alterar o valor do contexto */}
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | export default Home;
40 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/pages/Products.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 |
3 | import { CounterContext } from "../context/CounterContext";
4 |
5 | const Products = () => {
6 | const { counter } = useContext(CounterContext);
7 |
8 | return (
9 |
10 |
Lista de produtos
11 |
Valor contador: {counter}
12 |
13 | );
14 | };
15 |
16 | export default Products;
17 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/9_CONTEXT/context/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------