├── .github └── workflows │ └── arturito_ci.yml ├── README.md └── arturito ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── README.md ├── craco.config.js ├── package.json ├── public ├── index.html ├── manifest.json └── robots.txt ├── src ├── App.test.tsx ├── App.tsx ├── components │ ├── Header │ │ └── index.tsx │ ├── HomeSection │ │ └── index.tsx │ ├── PlanetsSection │ │ └── index.tsx │ ├── SectionSelector │ │ └── index.tsx │ └── Table │ │ └── index.tsx ├── containers │ ├── Main │ │ └── index.tsx │ └── paths.ts ├── index.css ├── index.tsx ├── react-app-env.d.ts ├── reportWebVitals.ts ├── setupTests.ts └── utils │ └── fetcher.ts ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.github/workflows/arturito_ci.yml: -------------------------------------------------------------------------------- 1 | name: Arturito Workshop CI 2 | 3 | on: [push] 4 | 5 | defaults: 6 | run: 7 | working-directory: arturito/ 8 | 9 | jobs: 10 | lint-job: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 6 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | 22 | - name: Get yarn cache directory path 23 | id: yarn-cache-dir-path 24 | run: echo "::set-output name=dir::$(yarn cache dir)" 25 | 26 | - name: Restore dependencies cache if it exists 27 | uses: actions/cache@v2 28 | id: yarn-cache 29 | with: 30 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 31 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 32 | restore-keys: | 33 | ${{ runner.os }}-yarn- 34 | 35 | - name: Install dependencies 36 | run: yarn --prefer-offline 37 | 38 | - name: Check prettier 39 | run: | 40 | yarn run prettier:complain 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ir a `/arturito`. 2 | 3 | 4 | **Maintainer:** @MartinCura -------------------------------------------------------------------------------- /arturito/.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 | -------------------------------------------------------------------------------- /arturito/.prettierignore: -------------------------------------------------------------------------------- 1 | # artifacts 2 | build 3 | coverage 4 | 5 | node_modules 6 | README.md 7 | 8 | .prettierrc.js 9 | -------------------------------------------------------------------------------- /arturito/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | jsxSingleQuote: false, 4 | printWidth: 80, 5 | tabWidth: 2, 6 | }; 7 | -------------------------------------------------------------------------------- /arturito/README.md: -------------------------------------------------------------------------------- 1 | # Arturito ✨ 2 | 3 | Instalar dependencias con `yarn` o `npm install`. 4 | 5 | Levantar aplicación con `yarn start` o `npm start`. 6 | 7 | ## Descripción 8 | 9 | Esta pequeña aplicación create-react-app con [TypeScript](https://typescriptlang.org) y otras bondades (como [TailwindCSS](https://tailwindcss.com), [swr](https://swr.vercel.app) y [prettier](https://prettier.io)) tiene una sección Planets que obtiene los planetas de Star Wars mediante la API libre (no necesita key) [SWAPI](https://www.swapi.it). 10 | 11 | ## Homework 👷 12 | 13 | En grupos de 2, trabajar con una metodología Git Flow para cumplir 4 funcionalidades nuevas: 14 | 15 | 1. Juntos, arreglar la vista existente Planets para que no muestre solo 3 planetas sino todos los que recibe. Esta parte debería ser rápida pero para que puedan explorar juntos el código existente. 16 | 2. Integrante 1 debe completar la sección Spaceships, instrucciones incluidas. 17 | 3. Al mismo tiempo, en paralelo, integrante 2 debe completar la sección People, instrucciones incluidas. 18 | 4. [Si llegan.] Agregar una nueva sección similar a las anteriores que aproveche otro endpoint de la API (o se puede incluir info de otra API a sugerencia). 19 | 20 | En todos los casos se debe trabajar en **ramas**, separando el trabajo en **commits pequeños**, con **PRs a main** que deben ser revisados por el otro integrante. ¡Lo normal es que haya varios comentarios! **Hacer las correcciones** que propone el compañero. 21 | 22 | 23 | *Sugerencia:* aprovechar lo dado para armar las nuevas vistas con el mínimo código posible, evitando repetir código que no haga falta. 24 | 25 | ¡Seguir las **buenas prácticas** de las que hablamos! 👉🏼 https://gist.github.com/henry-labs/fde7766161fb098a8e4edc04cc4caa97 26 | 27 | 28 | #### Advertencia al crear los PRs 29 | 30 | Por default cuando hacen PRs de GitHub desde un fork este apuntará al repo original. Asegúrense de que sea desde su feature branch a su main, siempre dentro del repo forkeado. La forma más fácil de hacer esto es revisar cuando estén creando el PR que la URL sea `https://github.com//wks-gitflow/...` (y no el usuario del repo original, en este caso `soyHenry` ❌). La _base branch_ debe decir **solo** `main` (o `master`), como en la siguiente imagen. 31 | 32 | ![image](https://user-images.githubusercontent.com/14017665/128374474-bbc72f3f-1a0c-4a63-8185-7c0b6110e2ef.png) 33 | 34 | 35 | **Maintainer:** @MartinCura 36 | -------------------------------------------------------------------------------- /arturito/craco.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | style: { 3 | postcss: { 4 | plugins: [require('tailwindcss'), require('autoprefixer')], 5 | }, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /arturito/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arturito", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "craco start", 7 | "build": "craco build", 8 | "test": "craco test", 9 | "prettier": "prettier --write .", 10 | "prettier:complain": "prettier --check .", 11 | "eject": "react-scripts eject" 12 | }, 13 | "eslintConfig": { 14 | "extends": [ 15 | "react-app", 16 | "react-app/jest" 17 | ] 18 | }, 19 | "browserslist": { 20 | "production": [ 21 | ">0.2%", 22 | "not dead", 23 | "not op_mini all" 24 | ], 25 | "development": [ 26 | "last 1 chrome version", 27 | "last 1 firefox version", 28 | "last 1 safari version" 29 | ] 30 | }, 31 | "dependencies": { 32 | "@craco/craco": "^6.2.0", 33 | "@testing-library/jest-dom": "^5.11.4", 34 | "@testing-library/react": "^11.1.0", 35 | "@testing-library/user-event": "^12.1.10", 36 | "@types/jest": "^26.0.15", 37 | "@types/node": "^12.0.0", 38 | "@types/react": "^17.0.0", 39 | "@types/react-dom": "^17.0.0", 40 | "antd": "^4.16.9", 41 | "axios": "^0.21.1", 42 | "react": "^17.0.2", 43 | "react-dom": "^17.0.2", 44 | "react-router-dom": "^5.2.0", 45 | "react-scripts": "4.0.3", 46 | "swr": "^0.5.6", 47 | "typescript": "^4.1.2", 48 | "web-vitals": "^1.0.1" 49 | }, 50 | "devDependencies": { 51 | "@types/axios": "^0.14.0", 52 | "@types/react-router-dom": "^5.1.8", 53 | "autoprefixer": "^9", 54 | "eslint-config-prettier": "8.3.0", 55 | "postcss": "^7", 56 | "prettier": "2.3.2", 57 | "tailwindcss": "npm:@tailwindcss/postcss7-compat" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /arturito/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 15 | 19 | 23 | 24 | 33 | Arturito - WKS Git Flow + Buenas Prácticas 34 | 35 | 36 | 37 |
38 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /arturito/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "https://img.icons8.com/plasticine/2x/fa314a/death-star.png", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "https://img.icons8.com/plasticine/2x/fa314a/death-star.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "https://img.icons8.com/plasticine/2x/fa314a/death-star.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 | -------------------------------------------------------------------------------- /arturito/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /arturito/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | 4 | import App from './App'; 5 | 6 | test('renders learn react link', () => { 7 | render(); 8 | const linkElement = screen.getByText(/learn react/i); 9 | expect(linkElement).toBeInTheDocument(); 10 | }); 11 | -------------------------------------------------------------------------------- /arturito/src/App.tsx: -------------------------------------------------------------------------------- 1 | import Header from './components/Header'; 2 | import MainContainer from './containers/Main'; 3 | 4 | const App = () => ( 5 |
6 |
7 | 8 | 9 |
10 | ); 11 | 12 | export default App; 13 | -------------------------------------------------------------------------------- /arturito/src/components/Header/index.tsx: -------------------------------------------------------------------------------- 1 | const Header = () => ( 2 |
3 |
4 | Henry logo 9 |

Arturito

10 |
11 | Death star 16 |
17 | ); 18 | 19 | export default Header; 20 | -------------------------------------------------------------------------------- /arturito/src/components/HomeSection/index.tsx: -------------------------------------------------------------------------------- 1 | const Button = ({ text, href }: { text: string; href: string }) => ( 2 | 3 |
4 | {text} 5 |
6 |
7 | ); 8 | 9 | const HomeSection = () => ( 10 |
11 |

¡Elegí una sección!

12 |
14 | ); 15 | 16 | export default HomeSection; 17 | -------------------------------------------------------------------------------- /arturito/src/components/PlanetsSection/index.tsx: -------------------------------------------------------------------------------- 1 | import useSWR from 'swr'; 2 | 3 | import { swGet } from '../../utils/fetcher'; 4 | import Table from '../Table'; 5 | 6 | const columns = [ 7 | { 8 | title: 'Name', 9 | dataIndex: 'name', 10 | key: 'name', 11 | }, 12 | { 13 | title: 'Climate', 14 | dataIndex: 'climate', 15 | key: 'climate', 16 | }, 17 | { 18 | title: 'Population', 19 | dataIndex: 'population', 20 | key: 'population', 21 | render: (population: string) => 22 | parseInt(population) 23 | ? parseInt(population).toLocaleString('es-AR') 24 | : population, 25 | }, 26 | { 27 | title: 'Residents count', 28 | dataIndex: 'residents', 29 | key: 'residents_count', 30 | render: (residents: string[]) => residents.length, 31 | }, 32 | ]; 33 | 34 | const Planets = () => { 35 | const { data, error } = useSWR('/planets', swGet); 36 | 37 | if (error) { 38 | return
Oh oh!
; 39 | } 40 | if (!data) { 41 | return
Loading...
; 42 | } 43 | 44 | return ( 45 |
46 | 47 | 48 | ); 49 | }; 50 | 51 | export default Planets; 52 | -------------------------------------------------------------------------------- /arturito/src/components/SectionSelector/index.tsx: -------------------------------------------------------------------------------- 1 | import { Link, useLocation } from 'react-router-dom'; 2 | 3 | import { paths, Path } from '../../containers/paths'; 4 | 5 | const SectionButton = ({ path }: { path: Path }) => { 6 | const location = useLocation(); 7 | return ( 8 | 9 |
14 | {path.name} 15 |
16 | 17 | ); 18 | }; 19 | 20 | const SectionSelector = () => ( 21 |
22 | {Object.keys(paths).map((path) => ( 23 | 24 | ))} 25 |
26 | ); 27 | 28 | export default SectionSelector; 29 | -------------------------------------------------------------------------------- /arturito/src/components/Table/index.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from 'antd'; 2 | import 'antd/dist/antd.dark.css'; 3 | 4 | interface Column { 5 | title: string; 6 | dataIndex: string; 7 | key: string; 8 | } 9 | 10 | const CustomTable = ({ columns, data }: { columns: Column[]; data: any[] }) => ( 11 |
12 | ); 13 | 14 | export default CustomTable; 15 | -------------------------------------------------------------------------------- /arturito/src/containers/Main/index.tsx: -------------------------------------------------------------------------------- 1 | import { Switch, Route, useLocation } from 'react-router-dom'; 2 | 3 | import { paths } from '../paths'; 4 | import SectionSelector from '../../components/SectionSelector'; 5 | import Home from '../../components/HomeSection'; 6 | import Planets from '../../components/PlanetsSection'; 7 | 8 | const MainContainer = () => { 9 | const location = useLocation(); 10 | const pathName = Object.keys(paths).find( 11 | (pathName) => paths[pathName].href === location.pathname 12 | ); 13 | 14 | const Title = () => ( 15 |

16 | {pathName ? paths[pathName].name : 'Where are we...?'} 17 |

18 | ); 19 | 20 | return ( 21 |
22 | 23 | 24 | 25 | 26 | <Switch> 27 | <Route path={paths.planets.href}> 28 | <Planets /> 29 | </Route> 30 | 31 | <Route path={paths.starships.href}> 32 | <div className="p-3"> 33 | <p className="font-bold text-xl"># TODO</p> 34 | <p> 35 | Agregar tabla con las starships sacadas de la API. Mostrar para 36 | cada starship: name, model, manufacturer, passengers, cantidad de 37 | films. Codear en un componente aparte tal como {'<Planets>'}. 38 | </p> 39 | <p> 40 | <a href="https://swapi.it/documentation#starships"> 41 | https://swapi.it/documentation#starships 42 | </a> 43 | </p> 44 | </div> 45 | </Route> 46 | 47 | <Route path={paths.people.href}> 48 | <div className="p-3"> 49 | <p className="font-bold text-xl"># TODO</p> 50 | <p> 51 | Agregar tabla con los personajes sacados de la API. Mostrar para 52 | cada persona: name, birth_year, height (en metros), cantidad de 53 | films. Codear en un componente aparte tal como {'<Planets>'}. 54 | </p> 55 | <p> 56 | <a href="https://swapi.it/documentation#people"> 57 | https://swapi.it/documentation#people 58 | </a> 59 | </p> 60 | </div> 61 | </Route> 62 | 63 | <Route path={paths.home.href}> 64 | <Home /> 65 | </Route> 66 | </Switch> 67 | </div> 68 | ); 69 | }; 70 | 71 | export default MainContainer; 72 | -------------------------------------------------------------------------------- /arturito/src/containers/paths.ts: -------------------------------------------------------------------------------- 1 | export interface Path { 2 | name: string; 3 | href: string; 4 | } 5 | 6 | export const paths: Record<string, Path> = { 7 | home: { name: 'Home', href: '/' }, 8 | planets: { name: 'Planets', href: '/planets' }, 9 | starships: { name: 'Starships', href: '/starships' }, 10 | people: { name: 'People', href: '/people' }, 11 | }; 12 | -------------------------------------------------------------------------------- /arturito/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /arturito/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter as Router } from 'react-router-dom'; 4 | 5 | import './index.css'; 6 | import App from './App'; 7 | import reportWebVitals from './reportWebVitals'; 8 | 9 | ReactDOM.render( 10 | <React.StrictMode> 11 | <Router> 12 | <App /> 13 | </Router> 14 | </React.StrictMode>, 15 | document.getElementById('root') 16 | ); 17 | 18 | // If you want to start measuring performance in your app, pass a function 19 | // to log results (for example: reportWebVitals(console.log)) 20 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 21 | reportWebVitals(); 22 | -------------------------------------------------------------------------------- /arturito/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="react-scripts" /> 2 | -------------------------------------------------------------------------------- /arturito/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /arturito/src/setupTests.ts: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /arturito/src/utils/fetcher.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | // Possible alternative: 'https://swapi.dev/api' 4 | const baseURL = 'https://www.swapi.it/api'; 5 | 6 | export const swGet = (url: string) => 7 | axios.get(url, { baseURL }).then((res) => res.data); 8 | -------------------------------------------------------------------------------- /arturito/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // mode: 'jit', 3 | purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | }; 13 | -------------------------------------------------------------------------------- /arturito/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": false, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"] 20 | } 21 | --------------------------------------------------------------------------------